前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++静态私有字段】+【C# <Module>.cctor】+【C++ if(条件断点)】

【C++静态私有字段】+【C# <Module>.cctor】+【C++ if(条件断点)】

作者头像
江湖评谈
发布2024-06-11 17:32:00
510
发布2024-06-11 17:32:00
举报
文章被收录于专栏:天下风云天下风云

前言

最近进行托管和非托管频繁操作,遇到了一些坑记录下。分别为标题标注的:C++静态私有字段访问,C#的全局静态构造函数.cctor,以及C++ if(延伸的条件断点)。这其中的两个C++问题,分别对应C#分析下。且了解下 <Module>.cctor的原理。

C++静态私有字段访问

例子:

代码语言:javascript
复制
class AAA
{
  private:
     static AAA aa;
     static int i;
     char ar;
  public:
     static AAA* get3a();
     static int getint();
     char arr(){
       return ar;
     }
};
//int AAA::i = 10;
//AAA AAA::aa;
int AAA::getint()
{
  return i;
}
AAA* AAA::get3a() 
{
  return &aa;
}
int main(int argc,char** argv)
{

  AAA::getint();
  AAA::get3a();
}

注意上面代码,注释的两行。

代码语言:javascript
复制
//int AAA::i = 10;
//AAA AAA::aa;

i和aa正是类AAA的私有字段,如果不对它们进行全局赋值,则VC++编译器会提示

代码语言:javascript
复制
无法解析的外部符号 "private: static int AAA::i" (?i@AAA@@0HA)
无法解析的外部符号 "private: static class AAA AAA::aa" (?aa@AAA@@0V1@A)  

然C#对于静态私有字段的访问,如下即可,不需要全局设置其值。如果以C#的写法应用在C++上,这是一个坑,需要注意。不得不说在面向对象方面,C#的爽点还是满满的。

代码语言:javascript
复制
public class AAA
 {
    private static AAA aa;
    private static int i;
    public static int getint()
    {
        return i;
    }
}
static void Main(string[] args)
 {
    AAA.getint();
}

C# <Module>..cctor

<Module>模块是一个虚拟的模块,它一般放在.NET目录-》数据流-》#~-》表TypeDef表项里面。这里的.cctor函数是这个模块的静态默认构造函数。它运行在托管Main入口之前,运行在System.Private.Corelib.dll之后。如果你想要在托管Main函数之前做一些事情,它是首选。C#代码里面不能够编辑它,可以通过Mono.Cecil对托管DLL添加这个函数。

代码语言:javascript
复制
static void AddModuleCCtor(string ModulePath)
{
    string assemblyPath = ModulePath+ "\\ConsoleApp5.dll";  // 请替换为您的目标程序集路径
    AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyPath);
    ModuleDefinition module = assembly.MainModule;

    TypeDefinition moduleType = module.Types.FirstOrDefault(t => t.Name == "<Module>");
    if (moduleType == null)
    {
        moduleType = new TypeDefinition("", "<Module>", TypeAttributes.NotPublic | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit, module.TypeSystem.Object);
        module.Types.Add(moduleType);
    }
    
    MethodDefinition cctor = moduleType.Methods.FirstOrDefault(m => m.Name == ".cctor");
    if (cctor == null)
    {
         cctor = new MethodDefinition(".cctor",
         MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
         module.TypeSystem.Void);
         moduleType.Methods.Add(cctor);
         
         ILProcessor il = cctor.Body.GetILProcessor();
         il.Append(il.Create(OpCodes.Ldstr, "Module static constructor is called."));
         il.Append(il.Create(OpCodes.Call, module.ImportReference(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }))));
         il.Append(il.Create(OpCodes.Ret));
     }
     else
     {
         ILProcessor il = cctor.Body.GetILProcessor();
         var nop = cctor.Body.Instructions.FirstOrDefault(i => i.OpCode == OpCodes.Nop);
         if (nop != null)
         {
            il.InsertBefore(nop, il.Create(OpCodes.Ldstr, "Module static constructor is called."));
            il.InsertBefore(nop, il.Create(OpCodes.Call, module.ImportReference(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }))));
           }
         }
        assembly.Write(ModulePath + "\\modified.dll");
}

这里有一个坑需要注意,比如本例中对ConsoleApp5.dll程序集进行了<Module>..cctor函数添加,形成了新的modified.dll托管DLL,这里需要注意,这两个托管DLL不能放在同一目录,否则会运行出错。保留你需要运行的托管DLL,以及xxx.runtimeconfig.json运行时文件即可。其它文件全部删掉,即可正常运行。在.NET8里面,这是一个巨坑,查找了很久才发现。

至于这个BUG是怎么引起的,这里先放一放,后面有时间再深入了解下。

C++ if 和C# if以及条件断点

先看下C++ if操作:

代码语言:javascript
复制
#include<stdio.h>
#include<Windows.h>
#include <string.h>

int main() 
{
    char str[] = "Hello, world!";
    char substr[] = "world";

    if (strstr(str, substr)) {
        printf("The substring \"%s\" is found in the string.\n", substr);
    }
    else {
        printf("The substring \"%s\" is not found in the string.\n", substr);
    }
    getchar();
    return 0;
}

这里的strstr函数是对字符串进行判断,是否包含的意思。如果包含则返回包含的字符串。一般的来说if括号里面是true或者false,但是这里if括号里面显然是字符串,它也被if视为true进行了分支执行。再看一段代码:

代码语言:javascript
复制
#include<stdio.h>
#include<Windows.h>
#include <string.h>
int main() 
{
    int a = 1000;
    if (a)
    {
        printf("true");
    }
    else
    {
        printf("false");
    }
    getchar();
    return 0;
}

这里依然输出的是true,在C++里面只要不是0或者NULL,if统统视为true。这跟C#是不同的,C#如下代码运行通不过:

代码语言:javascript
复制
static void Main(string[] args)
{
    int a = 1000;
    if(a)
    {
        Console.WriteLine("true");
    }
    else
    {
        Console.WriteLine("false");
    }
     Console.ReadLine();
}

vs直接报错,int不能转bool类型。由此延伸了一个条件断点,以上的<Module>..cctor在CLR里面的条件断点为例:

代码语言:javascript
复制
strstr(pMD->m_pszDebugClassName,"<Module>")

C++只需要strstr返回了字符串,就视为true。而字符串只能<Module>返回,所以当JIT Compile的pMD的m_pszDebugClassName是<Module>的时候就可以断下来了。当然如果有多个<Module>(这里是如果,实际应用一般只有一个),我们把pMD的函数名(m_pszDebugMethodName)带上,它的名称是:.cctor,那么条件断点如下:

代码语言:javascript
复制
strstr(pMD->m_pszDebugClassName,"<Module>")&&(*(pMD->m_pszDebugMethodName+1)=='c')&&(*(pMD->m_pszDebugMethodName+2)=='c')

结尾

以上是实际操作中遇到的坑,分享了三个知识点,希望对大家有所帮助。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 江湖评谈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • C++静态私有字段访问
  • C# <Module>..cctor
  • C++ if 和C# if以及条件断点
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档