.NET Main函数参数解释过程和特殊规则

news/2024/6/29 8:48:56
最近正在准备一个小型开发工具的发布工作(具体的内容请容我先卖个关子,等发布之后我会详细给大家介绍的)。在使用时不经意中发现,使用 .NET开发的命令行工具的对引号和反斜杠 / 和一般命令行程序有着不同的解释规则。举例来说,如果你在命令行下输入:
 
C:/> tool “C:/Program Files/”
 

实际上传入的参数是C:/Program Files” 。其实这里不仔细看可能发现不了问题。在原来的命令行中,第一个双引号代表一个参数的开始/结束,后面/”因为.NET的解释规则不同,代表实际的双引号,而非参数的开始/结束,因此最后的结果会多出一个双引号,并且缺少一个反斜杠。

内部,CLR 使用CommandLineToArgvW 来分析程序的命令行分离出各个参数,这个函数有着特殊的解释规则:
1.     2n 个反斜杠后面跟一个双引号代表n 个反斜杠
2.     2n+1 个反斜杠后面跟一个双引号代表n 个反斜杠加一个
3.     N 个反斜杠后面不跟双引号直接代表n 个反斜杠
这个规则比较绕,用例子的方式来解释就是:
命令行参数
实际参数
“C:/Program Files/”
C:/Program Files”
“C:/Program Files//”
C:/Program Files/
“C:/Program Files///”
C:/Program Files/”

因此,正确的方式是第二个,也就是”C:/Program Files//”

 
事实上,CLR 内部并没有直接调用CommandLineToArgvw ,而是直接实现了一个有着同等功能的函数SegmentCommandLine 。在Rotor 的源代码中可以找到它的实现,位于sscli20/clr/src/utilcode/util.cpp CLR 的主函数_CorExeMain 在执行主函数之前会调用CorCommandLine::SetArgvW ,这个函数会调用SegmentCommandLine 来分析命令行(经过简化):
 
// Set argvw from command line
/* static */

HRESULT CorCommandLine::SetArgvW(LPCWSTR lpCommandLine)

{
 

    HRESULT hr = S_OK;

if(!m_ArgvW) {

    // 分析命令行

        m_ArgvW = SegmentCommandLine(lpCommandLine, &m_NumArgs);

 

        // CLR特有的命令行处理,主要是和ClickOnce有关的

        if (m_ArgvW)

            hr = ParseCor();

        else

            hr = E_OUTOFMEMORY;

    }

 

    return hr;

}
 
 
真正在执行Main 主函数的时候,ClassLoader::RunMain 函数则会调用CorCommandLine::GetArgvW 获得之前分析得到的参数列表,并创建对应的托管String 数组并传递给Main (经过简化):
/* static */

HRESULT ClassLoader::RunMain(MethodDesc *pFD ,

                             short numSkipArgs,
                             INT32 *piRetVal,

                             PTRARRAYREF *stringArgs /*=NULL*/)

{
 

    wzArgs = CorCommandLine::GetArgvW(&cCommandArgs);

 

    // 创建一个托管数组

    StrArgArray = (PTRARRAYREF) AllocateObjectArray((cCommandArgs - numSkipArgs), g_pStringClass);

 

    // 创建对应的托管字符串并赋给托管数组的每个元素

    for( arg = numSkipArgs; arg < cCommandArgs; arg++) {

        STRINGREF sref = COMString::NewString(wzArgs[arg]);

        StrArgArray->SetAt(arg-numSkipArgs, (OBJECTREF) sref);

    }

 
MethodDescCallSite threadStart(pFD); // 准备调用MethodDesc指向的主函数(EntryPoint)
 

    ARG_SLOT stackVar = ObjToArgSlot(StrArgArray); // 将数组元素转为函数参数

 

*piRetVal = (INT32)threadStart.Call_RetArgSlot(&stackVar); // 调用主函数(EntryPoint

 

    return hr;

}
 
而最关键的SegmentCommandLine 函数代码则如下:
 
//---------------------------------------------------------------------

// Splits a command line into argc/argv lists, using the VC7 parsing rules.

//

// This functions interface mimics the CommandLineToArgvW api.

//
// If function fails, returns NULL.
//

// If function suceeds, call delete [] on return pointer when done.

//
//---------------------------------------------------------------------

LPWSTR *SegmentCommandLine(LPCWSTR lpCmdLine, DWORD *pNumArgs)

{

    STATIC_CONTRACT_NOTHROW;

    STATIC_CONTRACT_GC_NOTRIGGER;

    STATIC_CONTRACT_FAULT;

 
 

    *pNumArgs = 0;

 

    int nch = (int)wcslen(lpCmdLine);

 

    // Calculate the worstcase storage requirement. (One pointer for

    // each argument, plus storage for the arguments themselves.)

    int cbAlloc = (nch+1)*sizeof(LPWSTR) + sizeof(WCHAR)*(nch + 1);

    LPWSTR pAlloc = new (nothrow) WCHAR[cbAlloc / sizeof(WCHAR)];

    if (!pAlloc)

        return NULL;

 

    LPWSTR *argv = (LPWSTR*) pAlloc; // We store the argv pointers in the first halt

    LPWSTR pdst = (LPWSTR)( ((BYTE*)pAlloc) + sizeof(LPWSTR)*(nch+1) ); // A running pointer to second half to store arguments

    LPCWSTR psrc = lpCmdLine;

    WCHAR   c;

    BOOL    inquote;

    BOOL    copychar;

    int     numslash;

 

    // First, parse the program name (argv[0]). Argv[0] is parsed under

    // special rules. Anything up to the first whitespace outside a quoted

    // subtring is accepted. Backslashes are treated as normal characters.

    argv[ (*pNumArgs)++ ] = pdst;

    inquote = FALSE;

    do {

        if (*psrc == L'"' )

        {

            inquote = !inquote;
            c = *psrc++;
            continue;

        }

        *pdst++ = *psrc;

 

        c = *psrc++;

 

    } while ( (c != L'/0' && (inquote || (c != L' ' && c != L'/t'))) );

 

    if ( c == L'/0' ) {

        psrc--;

    } else {

        *(pdst-1) = L'/0';

    }

 

    inquote = FALSE;

 
 
 

    /* loop on each argument */

    for(;;)

    {

        if ( *psrc )

        {

            while (*psrc == L' ' || *psrc == L'/t')

            {
                ++psrc;
            }

        }

 

        if (*psrc == L'/0')

            break;              /* end of args */
 

        /* scan an argument */

        argv[ (*pNumArgs)++ ] = pdst;

 

        /* loop through scanning one argument */

        for (;;)

        {

            copychar = 1;

            /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote

               2N+1 backslashes + " ==> N backslashes + literal "

               N backslashes ==> N backslashes */

            numslash = 0;
            while (*psrc == L'//')
            {

                /* count number of backslashes for use below */

                ++psrc;
                ++numslash;
            }
            if (*psrc == L'"')
            {

                /* if 2N backslashes before, start/end quote, otherwise

                   copy literally */
                if (numslash % 2 == 0)
                {
                    if (inquote)
                    {

                        if (psrc[1] == L'"')

                        {
                            psrc++;    /* Double quote inside quoted string */
                        }
                        else
                        {

                            /* skip first quote char and copy second */

                            copychar = 0;
                        }
                    }
                    else
                    {
                        copychar = 0;       /* don't copy quote */
                    }
                    inquote = !inquote;
                }
                numslash /= 2;          /* divide numslash by two */
            }

   

            /* copy slashes */
            while (numslash--)
            {
                *pdst++ = L'//';
            }

   

            /* if at end of arg, break loop */

            if (*psrc == L'/0' || (!inquote && (*psrc == L' ' || *psrc == L'/t')))

                break;

   

            /* copy character into argument */

            if (copychar)
            {
                *pdst++ = *psrc;
            }
            ++psrc;

        }

 

        /* null-terminate the argument */

 

        *pdst++ = L'/0';          /* terminate string */

    }

 

    /* We put one last argument in -- a null ptr */

    argv[ (*pNumArgs) ] = NULL;

 

    _ASSERTE((BYTE*)pdst <= (BYTE*)pAlloc + cbAlloc);

    return argv;

}
有关CLR 执行Main 函数执行过程的更多有关内容我会在下篇Rotor 源码研究中详细解释,敬请关注。




http://www.niftyadmin.cn/n/3658059.html

相关文章

python 打印表格(如深度学习想打印混淆矩阵)

来源&#xff1a;prettytable confusion_matrix [[0 for j in range(4)] for i in range(4)]for i, j in zip(new_predicted, new_targets):confusion_matrix[i][j] 1tb pt.PrettyTable( ["predicted\\label", class1, class2, class3, class4])for i in range(4)…

SSD论文阅读笔记

SSD: Single Shot MultiBox Detector 论文arxiv链接&#xff1a;https://arxiv.org/pdf/1605.06409v2.pdf 亮点在于对多个size的featuremap产生各自scale的bbox&#xff1a; scale采取的是0-1的归一化scale&#xff0c;假设一共利用了m个大小不同的feature map&#xff0c;就…

Calling Convention的总结

因为经常需要和不同的Calling Convention打交道&#xff0c;前段时间整理了一下它们之间的区别&#xff0c;如下&#xff1a;清理堆栈参数压栈顺序命名规则 (MSVC)备注Cdecl调用者 (Caller)从右往左 FuncName因为是调用者清理Stack&#xff0c;因此允许变参 (如printf)Pascal被…

如何调试托管代码(managed code)和非托管代码(native code)混合的项目

在调试同时存在托管代码和非托管代码的程序的时候&#xff0c;需要特别注意选用适当的调试器的类型。因为看到类似的问题问得比较多&#xff0c;因此这里把做法写出来。在托管项目中在调试之前&#xff0c;需要打开对非托管代码调试的功能&#xff0c;这个选项在项目的属性的De…

NLP学习笔记(2)

如果你的数据集比较小&#xff0c;最好不要自己训练词向量&#xff0c;容易过拟合&#xff0c;直接download别人在某些语料库上训练好的词向量fix下来比较好但是如果你的数据集比较大&#xff0c;你可以随机初始化你的词向量并且在训练过程中训练他们window classification是最…

Silverlight 1.1正式更名为Silverlight 2.0

详情请参看下面两篇Blog&#xff1a;http://weblogs.asp.net/scottgu/archive/2007/11/29/net-web-product-roadmap-asp-net-silverlight-iis7.aspxhttp://blogs.msdn.com/tims/archive/2007/11/29/silverlight-1-1-is-now-silverlight-2-0.aspx

FPN阅读笔记

Feature Pyramid Networks for Object Detection arxiv链接&#xff1a;https://arxiv.org/pdf/1612.03144v2.pdf 网络结构示意图 核心思想是&#xff0c;高级语义信息在靠后的feature map&#xff0c;但是分辨率太小&#xff0c;导致小物体的特征几乎没有了&#xff0c;分辨…

ASP.NET 2.0 HttpHandler实现生成图片验证码(示例代码下载)

学习整理了一下(一).功能用HttpHandler实现图片验证码(二).代码如下1. 处理程序文件 ValidateImageHandler.ashx代码如下1 <% WebHandler Language"C#"Class"ValidateImageHandler"%>2 3 usingSystem;4 usingSystem.Web;5 usingSystem.Web.SessionSt…