批处理之家's Archiver

happy886rr 发表于 2017-2-22 10:09

批处理解释器ICMD.EXE

[i=s] 本帖最后由 happy886rr 于 2017-2-22 11:13 编辑 [/i]

无意中百度到了微软WINDOWS nt4的源码,见CSDN下载链接[url]http://download.csdn.net/detail/qbgao/3900606[/url],发现该源码是几乎非常完整的,包括基础dll的实现,内核的实现,cmd的实现都是完全可以编译。所以就在此基础上,结合wine的源码等多个开源项目,东拼西凑,耗时数小时,DIY了一个ICMD.EXE,其中编译需要用到20年前的库文件真是难搞。

此次DIY重点集成了原生API调用的功能,通过set /f开关实现。同时附送一个set /i开关方便批处理科学计算使用。小打小闹,源码几乎都是别人泄露的,我只把自己的revpolish计算器和一些小的修改加入,修复了一些大小写的问题,进行了多处代码改良,杂交,所以兼容性也可以,还有wine的影子。总之各种成分拼起来能用。

[quote]COPYRIGHT@2017~2019 REWRITE BY HAPPY, VERSION 1.0
ICMD.EXE

摘要:
=============================================================================
ICMD脚本解释器,时间仓促只增加了一小部分功能,就是set/i和set/f开关。
=============================================================================

[url=http://download.csdn.net/detail/happy886r/9760331]下载地址[/url]

用法:
-----------------------------------------------------------------------------
ICMD [使用手册]
-----------------------------------------------------------------------------

首先在批处理最顶端加上ICMD调用头
@IF [%1]==[] ("%~DP0ICMD.EXE" /C "%~F0" 1&EXIT)&::ICMD SCRIPT 2017~2019


然后基本语法同CMD.EXE,主要区别在于SET增加了 /I、/F开关


一、原生浮点计算开关SET /I

SET /I [结果值]=[浮点数学表达式]
-----------------------------------------------------------------------------
REM 增加原生浮点科学计算开关SET /I (使用自主研发的revpolish静逆波兰解析器)
set /i a=sin(torad(30)+cos(tan(3)))/7*6+exp(5)
echo %a%
-----------------------------------------------------------------------------


二、原生API调用开关 SET /F

SET /F [返回值]=[函数名] [参数] [参数] ...
-----------------------------------------------------------------------------
REM 普通字符串前加A作为标识,宽字符串前加L。如 L"1.ico"标识宽字符的 "1.ico",
    其他类型无需任何标识。

REM 一次把所有DLL都动态链入,省得每次呼叫。
set/f      =LinkDllW L"USER32" L"KERNEL32" L"GDI32" L"GDIPLUS" L"MSVCRT"

set/f hIcon=LoadImageW 0 L"1.ico" 1 0 0 16
set/f hCMD =GetConsoleWindow
set/f hDC  =GetDC %hCMD%
set/f      =DrawIconEx %hDC% %x% 0 %hIcon% 128 128 0 0 3
-----------------------------------------------------------------------------

备注:
-----------------------------------------------------------------------------
REM 由于源码比较古老,利用了VC98编译器的库文件才得以编译。完全抛弃 DLLCALL、
的古老模式。用最简洁的语法,调用C的函数。

抛弃了C语言的数据类型,只有字符串需要加标识符,ANSI字符串前加A,宽字符串前加L。
其他类型无需任何数据类型标识。
-----------------------------------------------------------------------------


支持的计算函数
-----------------------------------------------------------------------------
常数类
        pi    3.1415926535897932
        e     2.7182818284590452       

通用类
        rand  随机数
        round 四舍五入
        int   取整
        ceil  向上舍入
        floor 向下舍入
        abs   绝对值
        sqrt  开方
        lg    常用对数,以10为底
        ln    自然对数
        exp   e的次幂
        gamma 伽玛函数
        deg   度转弧度
        +     加
        -     减
        *     乘
        /     除
        %     取余数
        ^     次方
        !     阶乘

三角函数类
        sin、cos、tan   
        arcsin、arccos、arctan

双曲函数类
        sinh、cosh、tanh
        arcsinh、arccosh、arctanh
-----------------------------------------------------------------------------[/quote]

基本上取代了dll cll和capi。兼容95年后的各种windows带窗口的系统。我还在diy中,后续会改造出更强大的cmd新语法。
API调用格式更是简单到极点。
SET /F [返回值]=[函数名] [参数] [参数] ...[code]
@echo off
set/f=LinkDllW L"USER32" L"KERNEL32"
set/f=MessageBoxW 0 L"你好" L"message" 1
pause[/code]弹窗如此简单。

源码解读,在winNT的cenv.c中是set 命令的实现方式,我定位到如下地方[code]struct cmdnode *n ;
{
        TCHAR *tas ;    /* Tokenized argument string    */
        TCHAR *wptr ;   /* Work pointer                 */
        int i ;                 /* Work variable                */

        //
        // If extensions are enabled, things are different
        //
        if (fEnableExtensions) {
            tas = n->argptr;
            //
            // Find first non-blank argument.
            //
            if (tas != NULL)
            while (*tas && *tas <= SPACE)
                tas += 1;

            //
            // No arguments, same as old behavior.  Display current
            // set of environment variables.
            //
            if (!tas || !*tas)
                return(DisplayEnv()) ;

            //
            // See if /A switch given.  If so, let arithmetic
            // expression evaluator do the work.
            //
            if (!_tcsnicmp(tas, SetArithStr, 2))
                return SetArithWork(tas+2);

            //
            // See if first argument is quoted.  If so, strip off
            // leading quote, spaces and trailing quote.
            //
[/code]注意到[code]            if (!_tcsnicmp(tas, SetArithStr, 2))
                return SetArithWork(tas+2);
[/code]其中SetArithStr就是_TEXT("/A")照猫画虎,自己增加了一个开关_TEXT("/I"),其中的SetIRevpolish来自自己开发的静逆波兰计算器,以便实现浮点科学计算,链接[url=http://www.bathome.net/thread-42830-1-1.html]http://www.bathome.net/thread-42830-1-1.html[/url]。[code]
            if (!_tcsnicmp(tas, _TEXT("/I"), 2))
                return SetIRevpolish(tas+2);
[/code]在增加开关_TEXT("/F")[code]            if (!_tcsnicmp(tas, _TEXT("/F"), 2))
                return SetICallAPI(tas+2);
[/code]然后自己写个实现就搞定了。NT4时代确实没有SET /P开关,但是自己增加就行,然后自己写个读取stdin流的函数,set/p就此搞定。if中的EQU,GET...不支持小写,加个||就完美了。[code]
            if (!_tcsnicmp(tas, _TEXT("/P"), 2))
[/code]下面是开源项目wine的源码中关于cmd的核心部件,这个架构很 不错,就应该用switch来实现。[code]    switch (i) {

      case WCMD_ATTRIB:
        WCMD_setshow_attrib ();
        break;
      case WCMD_CALL:
        WCMD_call (p);
        break;
      case WCMD_CD:
      case WCMD_CHDIR:
        WCMD_setshow_default (p);
        break;
      case WCMD_CLS:
        WCMD_clear_screen ();
        break;
      case WCMD_COPY:
        WCMD_copy ();
        break;
      case WCMD_CTTY:
        WCMD_change_tty ();
        break;
      case WCMD_DATE:
        WCMD_setshow_date ();
        break;
      case WCMD_DEL:
      case WCMD_ERASE:
        WCMD_delete (p, TRUE);
        break;
      case WCMD_DIR:
        WCMD_directory (p);
        break;
      case WCMD_ECHO:
        WCMD_echo(&whichcmd[count]);
        break;
  ....
  ....
      default:
        WCMD_run_program (whichcmd, 0);
    }
[/code]

老刘1号 发表于 2017-4-23 10:22

话说,楼主可不可以出一个批的编译器a
批的效率太低……
写的话,其实支持内部命令和调用外部程序就行
如果不但能支持内部命令,还能支持外部程序打包那就更好了
期待ing...

happy886rr 发表于 2017-4-23 10:29

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=198625&ptid=43259]2#[/url] [i]老刘1号[/i] [/b]
直接学C语言吧,批处理就算能编译速度一样慢。

codegay 发表于 2017-4-23 10:31

:lol 有金钥匙,就不用拿铁钥匙了。

老刘1号 发表于 2017-4-23 10:35

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=198627&ptid=43259]3#[/url] [i]happy886rr[/i] [/b]


    C我感觉我需要入个门,
要不WIN32汇编和VBS的许多相关例子都看不懂……

happy886rr 发表于 2017-4-23 10:40

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=198632&ptid=43259]5#[/url] [i]老刘1号[/i] [/b]
C是开发第三方的基础,你如果打算写自己的第三方,就得学C,因为C写的第三方体积都在8kb到16kb之间,非常小巧,可以直接base加权码写在批处理中。

老刘1号 发表于 2017-4-23 10:43

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=198634&ptid=43259]6#[/url] [i]happy886rr[/i] [/b]


    我倒也不是为了开发第三方……

happy886rr 发表于 2017-4-23 10:47

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=198635&ptid=43259]7#[/url] [i]老刘1号[/i] [/b]
C可玩性很高,玩10年都不会腻,因为整个系统底层,包括cmd本身都是C写的。

老刘1号 发表于 2017-6-26 21:37

[i=s] 本帖最后由 老刘1号 于 2017-6-26 21:43 编辑 [/i]

实测多次调用函数会闪退
不过调一俩次没问题

测试代码:
[color=Red]关闭显示器.CMD[/color][code]@IF [%1]==[] ("ICMD.EXE" /C "%~F0" 1&EXIT)&@REM ICMD SCRIPT 2017~2019
cls&@echo off
echo 限你3秒钟内松开鼠标和键盘
ping 127.1 >nul
set/f=LinkDllW L"USER32"
Set /F 当前窗口句柄=GetForegroundWindow
set  /a MONITOR_OFF = 2
set  /a SC_MONITORPOWER = 0xF170
set  /a WM_SYSCOMMAND =  0x112
set /f                 =PostMessageA %当前窗口句柄% %WM_SYSCOMMAND% %SC_MONITORPOWER% %MONITOR_OFF%
exit[/code]

老刘1号 发表于 2017-6-27 18:09

[color=Red]BlockUserInput测试[/color][code]@IF [%1]==[] ("ICMD.EXE" /C "%~F0" 1&EXIT)&@REM ICMD SCRIPT 2017~2019
cls&@echo off
set/f=LinkDllW L"USER32"
Set "Block_ON=set/f=BlockInput 1"
Set "Block_OFF=set/f=BlockInput 0"

%Block_ON%
ping baidu.cn -n 6
%Block_OFF%
echo 任意键退出测试
pause[/code]

老刘1号 发表于 2017-6-27 18:11

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=198637&ptid=43259]8#[/url] [i]happy886rr[/i] [/b]


    求修BUG,函数调用超6次就会莫名退出

happy886rr 发表于 2017-6-27 22:12

[i=s] 本帖最后由 happy886rr 于 2017-6-27 22:14 编辑 [/i]

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=200689&ptid=43259]11#[/url] [i]老刘1号[/i] [/b]

我也无能为力,那是win95的源码,当时微软水平就那么菜、兼容性还差,源代码数万行,也不是一天能修好的。

老刘1号 发表于 2017-6-28 11:37

[i=s] 本帖最后由 老刘1号 于 2020-3-7 17:03 编辑 [/i]

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=200695&ptid=43259]12#[/url] [i]happy886rr[/i] [/b]


    如果不修复那就太可惜了……
这个DLL函数调用速度和免杀效果都完虐注入的capi系列……
强烈建议试着修复一下

老刘1号 发表于 2017-6-28 11:40

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=200695&ptid=43259]12#[/url] [i]happy886rr[/i] [/b]


    兼容性非常好啊,我的xp64不管是capi还是capix都无法运行,这个秒调用
就是稳定性……调用超6次就莫名闪退了

happy886rr 发表于 2017-6-28 12:09

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=200705&ptid=43259]14#[/url] [i]老刘1号[/i] [/b]
等我有时间了,就去修修。目前业务繁忙,未有余暇。

dengyuli 发表于 2018-10-17 19:11

您好,我在win10环境下使用了您的icmd
发现了一个很严重的问题:调用任何api都会导致内存泄漏
具体体现为:启动测试程序后,内存占用量随时间平稳上升
我的测试程序开启10分钟后,内存占用量从6M增加到147M(就算只调用Sleep这个api也会这样)
您给我们编写的样例代码也出现了内存泄漏(test1.cmd)
而用相同的调用方法,使用CAPIx,却不会出现这种情况
我非常希望您能够解决这个问题,毕竟CAPIx调用api的效率远没有icmd高

wujunkai 发表于 2019-4-15 22:31

我学c++的,可以给我源代码吗?
我试试……

马帅123 发表于 2020-3-7 15:15

下载地址要钱。。

马帅123 发表于 2020-3-7 15:16

没事了,去第三方库下载可以

slimay 发表于 2021-9-25 03:07

[b]回复 [url=http://www.bathome.net/redirect.php?goto=findpost&pid=200705&ptid=43259]14#[/url] [i]老刘1号[/i] [/b]
N年了, 请使用新版icmd, 兼容全部win系统, 不管几位都行 [url]http://www.bathome.net/viewthread.php?tid=60125&page=1&extra=#pid245617[/url]

页: [1]

Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.