给aar右键菜单添加子菜单.......

未解决  未打赏  
messias messias  悬赏: 5 金币
发布:2018-04-20 10:20:38 分类:问答
        这几天花了点时间,想把beautify-js格式化整合进aar中。之前见到有人把它做成扩展库,每次使用有点麻烦,而且总感觉太过于边缘化。当初的计划是,看能否整合进右键菜单,这样使用便利,当然看看能否在工具栏搞个按钮,再在菜单里添加一项。后面又想,看能否修改ui界面,搞个类似vscode黑色的版式,定制个人喜欢的字体。

        那么做起来还是相当麻烦,一开始考虑createwindow函数的钩子,后来发现这个函数也没什么有用的信息。后来花繁从简,直接子类化看看,发现很容易就实现了。当然,实现的仅仅是钩子而已。过程中出现几个问题:

1.添加进去的子项,是非有效状态,是反白的,这个问题想了很多办法也无解。
2.关于菜单的api都试过了,没法返回新增项的坐标或者Id,这样没法确定点击到的究竟是哪一条,后来用右键菜单窗口客户区坐标硬编码实现。也尝试过msaa来确定坐标,也失败了。当然,字体是标准字体,可以尝试ocr或者自己做特征来确定坐标,但这种方法还是算了。
3.右键菜单,会给主窗口发送一个弹出菜单初始化消息,仅仅识别这个消息还不够,因为标题栏右键菜单,标题图标菜单都会发出这个消息,如何识别又是个问题。后来通过判断菜单上原有项的文本解决了。
4.既然是格式化,那就需要有撤销功能,这个也做成了,但是这个整合进编辑工具栏中的撤销和重做才最好。这个也没找到解决办法。
5.添加了快捷键,本想使用自带的加速键库,但是那个库只支持aar自己的对象,当然修改一下库应该也行,但是太花时间了。
6.快捷键键盘消息会发送编辑窗口,但是考虑到撤销的问题,如果打开了多个aar文件,格式化和撤销问题要对应到某个文件,还不能错,也挺麻烦。
7.新建、关闭tab会给主窗口发送一个自定义消息,如果关闭到最后一个,会发送三个,这些都需要相应处理。如果文档关闭了,保存的文档历史列表,需要清理掉,这个放到了每次格式化的时候做一次清理。
8.beautify-js我测试了几个版本,找了个相对好用的。当然,可能格式化对于大多数来说不需要,也许我本人也不需要,但这只是个切入点,希望能对aar的ide做个扩展。
9.差点忘了,子类化库有个小问题,收到销毁消息后,就释放了回调函数指针,导致最后一次调用CallWindowProc是个Null,这样会崩溃。

        本想做个完善点的,考虑到时间拖得太久,人会失去耐心,先发上来抛砖引玉吧,就想做个安装器。安装器主要是在init文件里插入一行代码,用loadcode()就行,加载主要的aar文件;在tigger文件夹中新建一个文件夹,里面保存着主文件和格式化需要的文件,比如beautify-js,还有其他需要用到的文件。运行过程,一运行就给主窗口挂上钩子,如果有活动的编辑窗口,就给编辑窗口挂上勾子,为快捷键做准备;如果右键点击弹出菜单,则给弹出菜单窗口挂上勾子。

        最后说个令人丧气的结果,做安装器的过程中,发现点击新建工程或者开始页就崩溃(我只测试了这两个,其他的还未知),后来把所有的功能去掉,就是最简单的挂个钩子,所有的消息都下传,这样也不行,实在想不到是哪里的问题。但是考虑到花了这么多时间和精力,发上来给大家看看,一是大家看看哪里有啥问题不,二是如果校长有空,帮忙指点一下。


import ide;
import win;
import win.guid;
import com;
import web.script;
import key;

loadcodex("~\extensions\trigger\ideModify\WG.aardio");
/*
var oleacc=raw.loadDll("oleacc.dll");
var ole32=raw.loadDll("ole32.dll");
var accSupport=raw.loadDll(string.load("~\extensions\trigger\ideModify\accSupport.dll"),,"cdecl");
var IID_IAccessible=win.guid.fromString("{618736E0-3C3D-11CF-810C-00AA00389B71}");
var IID_IOleWindow=win.guid.fromString("{00000114-0000-0000-C000-000000000046}")
*/
var jsBeautify = string.load("~\extensions\trigger\ideModify\js_beautify.js");
var mainHwnd,appended,popHwnd,childHwnd=ide.getMainHwnd(),false;
var mainProcAddr,mainOld,popProcAddr,popOld,editorProcAddr,editorOld,tmpHwnd;
var editorTxtTab,editorIdxTab={},{};
var AFX_WM_ONCHANGE_ACTIVE_TAB=::User32.RegisterWindowMessage("AFX_WM_ONCHANGE_ACTIVE_TAB"); //获取自定义消息id
var fmt=function(){ //格式化函数
var editorHwnd=ide.getActiveCodeEditorHwnd(); //获取编辑窗口句柄
if !editorHwnd return 0;
var editorText = win.getText(editorHwnd, 1024 * 1024);
for key,val in editorTxtTab{
if !win.isWindow(tonumber(key)) editorTxtTab[key]=null; //如果文档窗口被关闭,清理其历史记录
}
if editorTxtTab[editorHwnd] { //如果文档窗口在记录里已存在,则新增
var size=table.push(editorTxtTab[editorHwnd],editorText);
if size>10 table.remove(editorTxtTab[editorHwnd]); //只保存10条历史记录
}else { //否则新增该文档的历史记录key
editorTxtTab[editorHwnd]={};
table.push(editorTxtTab[editorHwnd],editorText); //并把文档保存在数组尾部
}
editorIdxTab[editorHwnd]=0; //撤销次数清零
var js = web.script();
js.eval(jsBeautify);
winex.setText(editorHwnd, js.run("js_beautify", editorText));
return 1;
}


var unFmt=function(){ //撤销格式化函数
var editorHwnd=ide.getActiveCodeEditorHwnd();
if !editorHwnd return 0;
if editorTxtTab[editorHwnd] && #editorTxtTab[editorHwnd]>editorIdxTab[editorHwnd]{ //撤销次数不能超过已保存的文档个数
winex.setText(editorHwnd, editorTxtTab[editorHwnd][#editorTxtTab[editorHwnd]-editorIdxTab[editorHwnd]]);
editorIdxTab[editorHwnd]+=1; //记录撤销次数
}
}

var editorWndProc=function(hwnd,msg,wParam,lParam){
if msg==0x100/*_WM_KEYDOWN*/{
if (key.getState('CTRL') && wParam == 'Q'#) fmt(); //格式化快捷键ctrl+q
if (key.getState('CTRL') && wParam == 'W'#) unFmt(); //撤销格式化快捷键ctrl+w
}
return ::CallWindowProc(editorOld,hwnd,msg,wParam:0,lParam:0); //碰到过wParam,lParam有null的情况,这个函数就会报错
}

var popWndProc=function(hwnd,msg,wParam:0,lParam:0){
if msg==0x210/*WM_PARENTNOTIFY*/ && wParam==0x201/*WM_LBUTTONDOWN*/{
var clickCord=::POINT(::LOWORD(lParam),::HIWORD(lParam)); //右键菜单主窗口客户区内坐标
::ClientToScreen(popHwnd,clickCord); //转屏幕坐标
var cldRect=win.getClientRect(childHwnd); //得到右键菜单窗口的子窗口客户区坐标
win.toScreenRect(childHwnd,cldRect); //转屏幕坐标
var fmtRect=::RECT();
raw.convert(cldRect,fmtRect); //下面复制两个rect结构体
var unfmtRect=::RECT();
raw.convert(cldRect,unfmtRect);
fmtRect.top+=345; //设置连个菜单项的坐标区域
fmtRect.bottom-=27;
unfmtRect.top+=376;
var fmtBol,rct=::User32.PtInRect(fmtRect,clickCord.x,clickCord.y); //下面几句,鼠标左键点击是否在这两个菜单项区域内
var unfmtBol,rct=::User32.PtInRect(unfmtRect,clickCord.x,clickCord.y);
if fmtBol fmt();
if unfmtBol unFmt();
}
var ret=::CallWindowProc(popOld,hwnd,msg,wParam,lParam); //消息传递
if( msg == 0x2/*_WM_DESTROY*/ ) {
::SetWindowPointer(popHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,popOld); //如果窗口关闭,回复原窗口过程
popProcAddr=null;
}
return ret;
}

var mainWndProc=function(hwnd,msg,wParam:0,lParam:0){
if msg==0x117/*WM_INITMENUPOPUP*/{
var strLen=::User32.GetMenuString(wParam,2,null,0,0x00000400); //获取菜单第3条文本长度
var buf;
if strLen {
buf=raw.buffer(strLen*2,0);
::User32.GetMenuString(wParam,2,buf,strLen*2,0x00000400);
}
if strLen && string.fromUnicode(buf)=="复制全部到HTML"{ //判断是否为编辑窗口的右键菜单
if !appended{ //判断是否添加过菜单项
var ret=::User32.AppendMenu(wParam,0x800/*_MF_SEPARATOR*/,20,null); //添加分割线
var ret1=::User32.AppendMenu(wParam,0/*_MF_STRING*/,21,"Beautify Format CTRL+Q"); //添加格式化项
var ret1=::User32.AppendMenu(wParam,0/*_MF_STRING*/,22,"Undo Formated CTRL+W"); //添加撤销格式化项
appended=true; //添加过就置为true
}
var hwndTab=WG.chkHwnd(0,"Afx:00400000:800:00010003:00000010:00000000",,0); //查找右键菜单主窗口
for(i=1;#hwndTab;1){ //同类名的好几个
var parentHwnd,len=tonumber(hwndTab[i]); //所以需要用其子窗口做进一步筛选
childHwnd=win.findEx(parentHwnd,0,null,null);
if childHwnd && win.getClass(childHwnd)=="Afx:ToolBar:400000:8:10003:10"{
popHwnd=parentHwnd; //确定右键菜单主窗口
break;
}
}
if !popHwnd error("找不到指定窗口,请联系作者。");
//-----------------------
/*
var itfAddr={addr val} //这部分做msaa测试的
var hret=oleacc.AccessibleObjectFromWindow(childHwnd,0,IID_IOleWindow,itfAddr);
var oleWinDisp=com.QueryObject(topointer(itfAddr.val));
com.DumpTypeInfo(oleWinDisp);
*/

//------------------------
popProcAddr=raw.tostdcall(popWndProc,"int(addr,INT,ADDR,addr)");
popOld=::SetWindowPointer(popHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,popProcAddr);
if !popOld error("PopWnd Hook Failed");
}
}
if msg==AFX_WM_ONCHANGE_ACTIVE_TAB{ //判断是否为自定义消息
var editorHwnd=ide.getActiveCodeEditorHwnd();
if editorHwnd!=tmpHwnd{ //如果前后窗口非同一个
if editorProcAddr { //如果之前挂过钩子则恢复并释放
::SetWindowPointer(tmpHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,editorOld);
editorProcAddr=null;
}
if editorHwnd{ //如果存在活动编辑器窗口,则挂钩
editorProcAddr=raw.tostdcall(editorWndProc,"int(addr,INT,ADDR,addr)");
editorOld=::SetWindowPointer(editorHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,editorProcAddr);
if !editorOld error("EditorWnd Hook Failed");
tmpHwnd=editorHwnd; //保存本次编辑活动窗口句柄
}
}
}
var ret=::CallWindowProc(mainOld,hwnd,msg,wParam,lParam);
if( msg == 0x2/*_WM_DESTROY*/ ){
::SetWindowPointer(mainHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,mainOld);
mainProcAddr=null;
}
return ret;
}

mainProcAddr=raw.tostdcall(mainWndProc,"int(addr,INT,ADDR,addr)");
mainOld=::SetWindowPointer(mainHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,mainProcAddr);
if !mainOld error("MainWnd Hook Failed");

/*
如果自己写回调函数,不用子类化库,这里必须返回CallWindowProc的返回值。
不然会造成丢失消息,并且造成最小化、关闭等按钮的失效。
return ::CallWindowProc(mainOld,hwnd,msg,wParam,lParam);
*/
/*
Afx:ToolBar:400000:8:10003:10,这个类名,400000是程序实例句柄,8是窗口样式,
10003是光标,10是背景。都是16进制。
*/
/*
菜单项的识别号和位置号不同,位置号为系统给定,0为基。识别号为所有者编写,api中基本需要提供的是位置号。
*/


  • messias messias    
    Time:2018-04-20 10:37:13
    上面代码有点小问题,修改了:
    import ide;
    import win;
    import win.guid;
    import com;
    import web.script;
    import key;

    loadcodex("~\extensions\trigger\ideModify\WG.aardio");
    /*
    var oleacc=raw.loadDll("oleacc.dll");
    var ole32=raw.loadDll("ole32.dll");
    var accSupport=raw.loadDll(string.load("~\extensions\trigger\ideModify\accSupport.dll"),,"cdecl");
    var IID_IAccessible=win.guid.fromString("{618736E0-3C3D-11CF-810C-00AA00389B71}");
    var IID_IOleWindow=win.guid.fromString("{00000114-0000-0000-C000-000000000046}")
    */
    var jsBeautify = string.load("~\extensions\trigger\ideModify\js_beautify.js");
    var mainHwnd,appended,popHwnd,childHwnd=ide.getMainHwnd(),false;
    var mainProcAddr,mainOld,popProcAddr,popOld,editorProcAddr,editorOld,tmpHwnd;
    var editorTxtTab,editorIdxTab={},{};
    var AFX_WM_ONCHANGE_ACTIVE_TAB=::User32.RegisterWindowMessage("AFX_WM_ONCHANGE_ACTIVE_TAB"); //获取自定义消息id
    var fmt=function(){ //格式化函数
    var editorHwnd=ide.getActiveCodeEditorHwnd(); //获取编辑窗口句柄
    if !editorHwnd return 0;
    var editorText = win.getText(editorHwnd, 1024 * 1024);
    for key,val in editorTxtTab{
    if !win.isWindow(tonumber(key)) editorTxtTab[key]=null; //如果文档窗口被关闭,清理其历史记录
    }
    if editorTxtTab[editorHwnd] { //如果文档窗口在记录里已存在,则新增
    var size=table.push(editorTxtTab[editorHwnd],editorText);
    if size>10 table.remove(editorTxtTab[editorHwnd]); //只保存10条历史记录
    }else { //否则新增该文档的历史记录key
    editorTxtTab[editorHwnd]={};
    table.push(editorTxtTab[editorHwnd],editorText); //并把文档保存在数组尾部
    }
    editorIdxTab[editorHwnd]=0; //撤销次数清零
    var js = web.script();
    js.eval(jsBeautify);
    winex.setText(editorHwnd, js.run("js_beautify", editorText));
    return 1;
    }


    var unFmt=function(){ //撤销格式化函数
    var editorHwnd=ide.getActiveCodeEditorHwnd();
    if !editorHwnd return 0;
    if editorTxtTab[editorHwnd] && #editorTxtTab[editorHwnd]>editorIdxTab[editorHwnd]{ //撤销次数不能超过已保存的文档个数
    winex.setText(editorHwnd, editorTxtTab[editorHwnd][#editorTxtTab[editorHwnd]-editorIdxTab[editorHwnd]]);
    editorIdxTab[editorHwnd]+=1; //记录撤销次数
    }
    }

    var editorWndProc=function(hwnd,msg,wParam,lParam){
    if msg==0x100/*_WM_KEYDOWN*/{
    if (key.getState('CTRL') && wParam == 'Q'#) fmt(); //格式化快捷键ctrl+q
    if (key.getState('CTRL') && wParam == 'W'#) unFmt(); //撤销格式化快捷键ctrl+w
    }
    return ::CallWindowProc(editorOld,hwnd,msg,wParam:0,lParam:0); //碰到过wParam,lParam有null的情况,这个函数就会报错
    }

    var popWndProc=function(hwnd,msg,wParam,lParam){
    if msg==0x210/*WM_PARENTNOTIFY*/ && wParam==0x201/*WM_LBUTTONDOWN*/{
    var clickCord=::POINT(::LOWORD(lParam),::HIWORD(lParam)); //右键菜单主窗口客户区内坐标
    ::ClientToScreen(popHwnd,clickCord); //转屏幕坐标
    var cldRect=win.getClientRect(childHwnd); //得到右键菜单窗口的子窗口客户区坐标
    win.toScreenRect(childHwnd,cldRect); //转屏幕坐标
    var fmtRect=::RECT();
    raw.convert(cldRect,fmtRect); //下面复制两个rect结构体
    var unfmtRect=::RECT();
    raw.convert(cldRect,unfmtRect);
    fmtRect.top+=345; //设置连个菜单项的坐标区域
    fmtRect.bottom-=27;
    unfmtRect.top+=376;
    var fmtBol,rct=::User32.PtInRect(fmtRect,clickCord.x,clickCord.y); //下面几句,鼠标左键点击是否在这两个菜单项区域内
    var unfmtBol,rct=::User32.PtInRect(unfmtRect,clickCord.x,clickCord.y);
    if fmtBol fmt();
    if unfmtBol unFmt();
    }
    var ret=::CallWindowProc(popOld,hwnd,msg,wParam:0,lParam:0); //消息传递
    if( msg == 0x2/*_WM_DESTROY*/ ) {
    ::SetWindowPointer(popHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,popOld); //如果窗口关闭,回复原窗口过程
    popProcAddr=null;
    }
    return ret;
    }

    var mainWndProc=function(hwnd,msg,wParam,lParam){
    if msg==0x117/*WM_INITMENUPOPUP*/{
    var strLen=::User32.GetMenuString(wParam,2,null,0,0x00000400); //获取菜单第3条文本长度
    var buf;
    if strLen {
    buf=raw.buffer(strLen*2,0);
    ::User32.GetMenuString(wParam,2,buf,strLen*2,0x00000400);
    }
    if strLen && string.fromUnicode(buf)=="复制全部到HTML"{ //判断是否为编辑窗口的右键菜单
    if !appended{ //判断是否添加过菜单项
    var ret=::User32.AppendMenu(wParam,0x800/*_MF_SEPARATOR*/,20,null); //添加分割线
    var ret1=::User32.AppendMenu(wParam,0/*_MF_STRING*/,21,"Beautify Format CTRL+Q"); //添加格式化项
    var ret1=::User32.AppendMenu(wParam,0/*_MF_STRING*/,22,"Undo Formated CTRL+W"); //添加撤销格式化项
    appended=true; //添加过就置为true
    }
    var hwndTab=WG.chkHwnd(0,"Afx:00400000:800:00010003:00000010:00000000",,0); //查找右键菜单主窗口
    for(i=1;#hwndTab;1){ //同类名的好几个
    var parentHwnd,len=tonumber(hwndTab[i]); //所以需要用其子窗口做进一步筛选
    childHwnd=win.findEx(parentHwnd,0,null,null);
    if childHwnd && win.getClass(childHwnd)=="Afx:ToolBar:400000:8:10003:10"{
    popHwnd=parentHwnd; //确定右键菜单主窗口
    break;
    }
    }
    if !popHwnd error("找不到指定窗口,请联系作者。");
    //-----------------------
    /*
    var itfAddr={addr val} //这部分做msaa测试的
    var hret=oleacc.AccessibleObjectFromWindow(childHwnd,0,IID_IOleWindow,itfAddr);
    var oleWinDisp=com.QueryObject(topointer(itfAddr.val));
    com.DumpTypeInfo(oleWinDisp);
    */

    //------------------------
    popProcAddr=raw.tostdcall(popWndProc,"int(addr,INT,ADDR,addr)");
    popOld=::SetWindowPointer(popHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,popProcAddr);
    if !popOld error("PopWnd Hook Failed");
    }
    }
    if msg==AFX_WM_ONCHANGE_ACTIVE_TAB{ //判断是否为自定义消息
    var editorHwnd=ide.getActiveCodeEditorHwnd();
    if editorHwnd!=tmpHwnd{ //如果前后窗口非同一个
    if editorProcAddr { //如果之前挂过钩子则恢复并释放
    ::SetWindowPointer(tmpHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,editorOld);
    editorProcAddr=null;
    }
    if editorHwnd{ //如果存在活动编辑器窗口,则挂钩
    editorProcAddr=raw.tostdcall(editorWndProc,"int(addr,INT,ADDR,addr)");
    editorOld=::SetWindowPointer(editorHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,editorProcAddr);
    if !editorOld error("EditorWnd Hook Failed");
    tmpHwnd=editorHwnd; //保存本次编辑活动窗口句柄
    }
    }
    }
    var ret=::CallWindowProc(mainOld,hwnd,msg,wParam:0,lParam:0);
    if( msg == 0x2/*_WM_DESTROY*/ ){
    ::SetWindowPointer(mainHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,mainOld);
    mainProcAddr=null;
    }
    return ret;
    }

    mainProcAddr=raw.tostdcall(mainWndProc,"int(addr,INT,ADDR,addr)");
    mainOld=::SetWindowPointer(mainHwnd,0xFFFFFFFC/*_GWL_WNDPROC*/,mainProcAddr);
    if !mainOld error("MainWnd Hook Failed");

    /*
    如果自己写回调函数,不用子类化库,这里必须返回CallWindowProc的返回值。
    不然会造成丢失消息,并且造成最小化、关闭等按钮的失效。
    return ::CallWindowProc(mainOld,hwnd,msg,wParam,lParam);
    */
    /*
    Afx:ToolBar:400000:8:10003:10,这个类名,400000是程序实例句柄,8是窗口样式,
    10003是光标,10是背景。都是16进制。
    */
    /*
    菜单项的识别号和位置号不同,位置号为系统给定,0为基。识别号为所有者编写,api中基本需要提供的是位置号。
    */


  • Admin Admin    
    Time:2018-04-20 15:29:38
    官方不允许给aardio的IDE做修改的,可以直接向作者申请开放某些接口,打赏点加班费即可.
    用户对这些接口有兴趣:
    1.代码编辑框的样式配置,最好是可以自行加载配置文件,各种暗黑系粉红系酷炫尽情发挥
    2.代码编辑框的事件接口,比如选择某个单词时,扩展面板可以立即显示它的用法和示例
    3.右键菜单,选择某个单词时,右键打开库函数文档并跳到对应位置
    4.扩展面板,用户发挥自己的想像力,给它加上搜索结果列表,调试信息啥的
[ 发单/接单 ]
换IP投票软件
PC蛋蛋自动挂机投注
新浪微博发微博显示尾巴的方法,比如显示来自iphone 7
百度文库批量自动上传软件
一点资讯app刷阅读量/评论/收藏/订阅功能/手机号注册
定做一个阿迪达斯官网注册器(需要破点击文字式验证码)
定制人人网自动注册/修改资料/采集/私聊软件
酷狗繁星直播网页协yi
YY多功能刷订阅刷粉丝
滑块验证码本地识别
狼人杀POST QQ登录注册 获取金币数量
做个贴吧发发帖的软件懂的来
今日头条账号保存cookie
抖音粉丝软件定做,只要粉丝
百家号自媒体发文软件定制
哔哩哔哩播放量
读取TB某个商品上架时间和相关信息
网易博客软件定制
定制天涯论坛发帖软件
定制今日头条批量自动发私信软件
[ 站内搜索 ]
[ 最近热帖 ]
万能助手 -- 扩展库大全集 13735
用aardio创建web工程图文讲解(1) 12816
电脑编程入门自学:Fiddler https 抓包时提示创建根证书不成功问题彻底解决(https插件dll方式) 11724
aardio绘图演示 11391
通过chrome.dll中间件控制外部chrome浏览器 10104
我常用的aardio技巧 9762
《边学C语言边赚钱——简码编程入门教程》系列集合 9607
aardio使用http或whttp进行get/post请求时经常cookies失效怎么办?__电脑计算机编程入门教程自学 9312
[源码下载]简码视频加密解密播放工具个人版v1.0发布,永久免费开源的知识变现神器 8551
python人工智能爬虫系列:怎么查看python版本_电脑计算机编程入门教程自学 8038
编程入门教程:aardio批量上传文件并显示进度条 7870
aardio调用nodejs的ws模块做一个简单的聊天通信示例 7024
电脑计算机编程入门教程自学:原生JavaScript判断字符是否为A-Za-z英文字母 6900
电脑计算机编程入门教程自学:腾讯tx或极验geetest滑块按住拖动完成拼图验证成功破解思路及源码 6496
乐玩插件AARDIO调用 6073
[ 近期热答 ]
电脑计算机编程入门教程自学:什么是buffer缓冲区? 1
aardio_代码编辑框书签管理器开源 1
aardio_怎么用ide库从代码编辑框中取出指定行的源码? 1
aardio_codepage代码页编码乱码暴力猜解工具 1
aardio内嵌echarts图表添加鼠标事件响应功能 2
aardio列表框listbox_模糊查找和精确查找 1
8亿QQ绑定手机泄露:通过腾讯QQ号查询QQ绑定的手机号码漏洞! 1
carl listviewex调用例子----------源码搬运工 2
Aardio内嵌Electron浏览框怎么正确添加启动参数? 1
Aardio取汉字的字节数或字符数 1
怎么实现mssql图片数据的读写 2
aardio_从49个数字里选六个和值为150的不重复的数字 1
在嵌入wps的时候,多了一个 透明的边框,这个怎么消除它 1
万能助手入门帮助教程:学会科学地管理工作文件 1
Aaardio开发内嵌Electron浏览框放服务器上无法下载组件的解决办法 1