欢迎光临 Rick 's BLOG
日志首页  | C# .Net编程  | 原创作品  | 生活点滴  | C\C++相关  | 多媒体相关※ERMP  | VB相关  | 其它运维与编程  |  留言簿
让 .Net 程序 脱离 .net framework框架 运行的方法 dotNet MSIL中的一些不常见IL指令
未知  [转载]Calling printf from C# - The tale of the hidde
[ 发布日期:18年前 (2007-08-19) ]   [ 来自:SSCLI ] [分类:原创作品]
Browsing the SSCLI can be enlighting from time to time (if not all the time). Take a look at the following function implemented in console.cs:
[
HostProtection(UI=true)]
[CLSCompliant(false)] 
publicstaticvoid WriteLine(String format, Object arg0, Object arg1, Object arg2,Object arg3, __arglist) 
{
   Object[] objArgs;
   int argCount;

   ArgIterator args = newArgIterator(__arglist);

   //+4 to account for the 4 hard-coded arguments at the beginning of the list.
   argCount = args.GetRemainingCount() + 4;

   objArgs = newObject[argCount];

   //Handle the hard-coded arguments
   objArgs[0] = arg0;
   objArgs[1] = arg1;
   objArgs[2] = arg2;
   objArgs[3] = arg3;

   //Walk all of the args in the variable part of the argument list.
   for (int i=4; i<argCount; i++) {
      objArgs = TypedReference.ToObject(args.GetNextArg());
   }

   Out.WriteLine(format, objArgs);
}

Hmm, looks weird isn't it? Using that dark keyword __arglist (it really is a keyword, look at the keyword coloring of VS2005). Not to talk about TypedReference and ArgIterator yet. So, what is it and what does it do?
Compile the following piece of code:
class Program
{
   static
void Main(string[] args)
   {
      Foo(__arglist(1, 2, 3));
   }

   staticvoid Foo(__arglist)
   {
      ArgIterator iter = newArgIterator(__arglist);
      for (int n = iter.GetRemainingCount(); n > 0; n--)
         Console.WriteLine(TypedReference.ToObject(iter.GetNextArg()));
   }
}

Output will just print
1
2
3
on the screen.
Now take a look at the IL code:
.method private hidebysig static vararg void 
Foo() cil managed
{
   .locals init ([0] valuetype [mscorlib]System.ArgIterator iter,
      [1] int32 n,
      [2] bool CS$4$0000)
   IL_0000: nop
   IL_0001: ldloca.s iter
   IL_0003: arglist
   IL_0005: call instance void [mscorlib]System.ArgIterator::.ctor(valuetype [mscorlib]System.RuntimeArgumentHandle)
   ...
} // end of method Program::Foo
Welcome to mysteria lane again: RuntimeArgumentHandle pops up. Information on MSDN reveals the C/C++ programming language support it's intended for. Taking a look at the CIL instruction set in Partition III, section 3.4 of the ECMA 355 CLI standard learns us that arglist is used to [i]return argument list handle for the current method
. So, by calling arglist (metadata shows that the method supports this, cf. vararg) a pointer to the argument list is pushed on the stack.
All of this mysterious stuff makes one wonder about possible other hidden secrets, and guess what: tokens.h in the csharp folder of the SSCLI reveals four such keywords:
TOK(L
"__arglist" , TID_ARGS , TFF_MSKEYWORD | TFF_TERM , 0 , NOPARSEFN , OP_NONE , OP_NONE , OP_ARGS , KEYWORD )
TOK(L"__makeref" , TID_MAKEREFANY , TFF_MSKEYWORD | TFF_TERM , 0 , NOPARSEFN , OP_NONE , OP_NONE , OP_MAKEREFANY , KEYWORD )
TOK(L"__reftype" , TID_REFTYPE , TFF_MSKEYWORD | TFF_TERM , 0 , NOPARSEFN , OP_NONE , OP_NONE , OP_REFTYPE , KEYWORD )
TOK(L"__refvalue" , TID_REFVALUE , TFF_MSKEYWORD | TFF_TERM , 0 , NOPARSEFN , OP_NONE , OP_NONE , OP_REFVALUE , KEYWORD )

Don't worry about the macro stuff in here. The basic usage of each of these (undocumented == don't use) keywords is shown below (you can find out about the syntax by inspecting and understanding the tokens.h file):
int
 i = 1;

TypedReference tr = __makeref(i); // tr = &i
__refvalue(tr, int) = 2; // *tr = 2

Console.WriteLine(TypedReference.ToObject(tr));
Console.WriteLine(__refvalue(tr,int)); // kind of a "cast back" (&agrave; la 'tr as int')

Console.WriteLine(TypedReference.GetTargetType(tr));
Console.WriteLine(__reftype(tr)); // i.GetType()

This prints out:
2
2
System.Int32
System.Int32
IL analysis reveals four dark IL instructions once more:
.locals init ([0] int32 i,
   [1] typedref tr)
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: ldloca.s i
IL_0005: mkrefany [mscorlib]System.Int32
IL_000a: stloc.1
IL_000b: ldloc.1
IL_000c: refanyval [mscorlib]System.Int32
IL_0011: ldc.i4.2
IL_0012: stind.i4
IL_0013: ldloc.1
IL_0014: call object [mscorlib]System.TypedReference::ToObject(typedref)
IL_0019: call void [mscorlib]System.Console::WriteLine(object)
IL_001f: ldloc.1
IL_0020: refanyval [mscorlib]System.Int32
IL_0025: ldind.i4
IL_0026: call void [mscorlib]System.Console::WriteLine(int32)
IL_002c: ldloc.1
IL_002d: call class [mscorlib]System.Type [mscorlib]System.TypedReference::GetTargetType(typedref)
IL_0032: call void [mscorlib]System.Console::WriteLine(object)
IL_0038: ldloc.1
IL_0039: refanytype
IL_003b: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0040: call void [mscorlib]System.Console::WriteLine(object)
mkrefany - Push a typed reference to stack.Pop() of type arg[0] onto the stack (4.18) - On line IL_0005 this pops the address of i (obtained in IL_0003) from the stack and pushes a typed reference ([1]) to i on the stack.
refanyval - Push the address stored in a typed reference (4.22) - On line IL_000c this pops the typed reference ([1]) from the stack and pushes the stored address (&i) on the stack (cf. stind in line IL_0012 uses the address to do the assignment of 2, kind of *tr = 2).
refanytype - Push the type token stored in a typed reference (4.21) - On line IL_0039 this pops the typed reference ([1]) from the stack and pushes the type token (i.e. a handle to the type) on the stack. Line IL_003b uses this token to construct a Type object out of it.
Now, what can we do with all of this? Nothing much to worry about; you haven't missed yet another powerful feature in C#. As we do have the params keyword at our service in C#, we don't need this construct. Anyway, when browsing the SSCLI source this knowledge can be handy to have.
However, there is one nice (but useless) thing you can do:
[
DllImport("msvcrt40.dll")]
publicstaticexternint printf(string format, __arglist);

staticvoid Main(string[] args)
{
   printf("Hello %s!\n", __arglist("Bart"));
}

Woohoo, "Hello Bart!" on my screen. Guess I'll stick with Console.WriteLine anyhow :-). And you should too; it's very easy to shoot yourself in the foot using undocumented stuff, needless to say so.
Time to return to reality and wipe out this journey through the dark side of .NET from our memories. Happy __arglist-less coding!

http://bartdesmet.net/blogs/bart/archive/2006/09/28/4473.aspx
http://www.codeproject.com/dotnet/pointers.asp
引用通告地址 (0):
复制引用地址https://www.rickw.cn/trackback/186
复制引用地址https://www.rickw.cn/trackback/186/GBK
[ 分类:原创作品  | 查看:1095 ]

暂时没有评论,快来发表一个评论吧。
发表评论
作者:   用户:[访客] 
评论:

表  情
禁止表情 | 禁止UBB | 禁止图片 | 识别链接
对不起,你没有权限上传附件!
验证:
 
PoweredBy R-Blog V1.00 © 2004-2024 WWW.RICKW.CN, Processed in second(s) , 7 queries    京ICP备17058477号-5