首页 > 解决方案 > 从第三个应用程序获取浏览器 URL

问题描述

我正在开发 MacOS 应用程序,我想知道是否有办法从中获取活动浏览器的 url。应用程序是在 C++ 中完成的。

我想在不必使用 AppleScript 的情况下获得它。

那可能吗?

谢谢

标签: c++macosurlapplescript

解决方案


这不是一个简单的答案,但好消息是“是的,无需使用 AppleScript 就可以做到这一点”,而坏消息是“您必须首先使用 AppleScript”。

让我详细说明一下:浏览器应用程序通常有一个 Applescript 字典(您可以使用/Applications/Utilities文件夹中的“脚本编辑器”应用程序查看它。这是 Google Chrome 的字典的样子:Google Chrome 的脚本编辑器字典 您会看到我找到了“tab”类,在那里你会看到 URL 属性。

所以你需要做的是首先编写一个 AppleScript来为你的目标浏览器获取窗口。然后,当它工作时,您需要将 AppleScript 转换为底层的原始 AppleEvents(AppleScripts 编译到的内容)。AppleScripts 和 AppleEvents 都可以在代码中完成。

据我所知,您真正正在寻找的答案(例如“我可以使用一些绝密 API 或将我的代码中的 URL 发送到我本地机器上的某个开放端口来查询浏览器吗?”)不存在. AppleScript 和 AppleEvents 是 Apple 提供的用于自动化大多数应用程序的长期方式,您需要利用它。

如果您决定继续使用 AppleScript 并可能将它们转换为 AppleEvents,这就是我要做的。

1) 查找公开可用的 AppleScripts 来完成您想要的工作。

2) 将 AppleScript 转换为 AppleEvents

3) 对 AppleEvents 进行编码。

对于 1,这是一个遍历 Safari 中每个窗口的脚本,这是我从本教程中获得的:

tell application "Safari"

--Variables
set windowCount to number of windows
set docText to ""

--Repeat for Every Window
repeat with x from 1 to windowCount
set tabCount to number of tabs in window x

--Repeat for Every Tab in Current Window
repeat with y from 1 to tabCount
--Get Tab Name & URL
set tabName to name of tab y of window x
set tabURL to URL of tab y of window x
end repeat

end repeat
end tell

对于第 2 步,我认为您可以使用脚本编辑器中的附件视图窗格来查看生成的原始事件和结果。

对于第 3 步,我用 C 编写的这段古老代码将获取给定窗口 ID 的浏览器窗口的 URL。

OSStatus CreateTheAppleEventForGetURL( OSType appSignature, long windowid, char **retval) 
{
    OSErr               err = noErr;
    AEAddressDesc       targetAddress;
    AEDescList          replyDesc = { typeNull, nil };
    AEKeyword           keyword;
    DescType            desiredClass;   
    AEDesc              replySingle, theOptionalAttributeDesc, theSeldDesc,theObjSpec,theThirdObjSpec,theSecondObjSpec,theFormDesc,theNullDesc;
    AppleEvent          theEvent, reply;
    AEIdleUPP           theIdleProc;
    Boolean             gotReply = false;
    long                errNumber=0;
    long                buffer;
    Size                actualSize;
    char                *result = NULL;

    theIdleProc = NewAEIdleUPP((AEIdleProcPtr)&TheIdleFunction );
    if( NULL != theIdleProc )
    {
        err = AECreateDesc( typeApplSignature, &appSignature, sizeof( appSignature ), &targetAddress );

        if( noErr == err )
        {
            err = AECreateAppleEvent( 'core', 'getd', &targetAddress, kAutoGenerateReturnID, kAnyTransactionID, &theEvent );
            buffer = 0x10000;
            err = AECreateDesc('magn', &buffer, 4, &theOptionalAttributeDesc);
            if( noErr == err )
            {
                err = AECreateDesc(typeNull, nil, 0, &theNullDesc);
                desiredClass = 'cwin';
                buffer = 'ID  ';
                err = AECreateDesc('enum',&buffer,4,&theFormDesc);
                buffer = windowid;
                err = AECreateDesc(typeLongInteger,&buffer,4,&theSeldDesc);
                err = CreateObjSpecifier(desiredClass,&theNullDesc,'ID  ',&theSeldDesc,true,&theThirdObjSpec);
                buffer = 'docu';
                err = AECreateDesc(typeType,&buffer,4,&theSeldDesc);
                desiredClass = 'prop';
                err = CreateObjSpecifier(desiredClass,&theThirdObjSpec,'prop',&theSeldDesc,true,&theSecondObjSpec);
                buffer = 'prop';
                err = AECreateDesc('enum',&buffer,4,&theFormDesc);
                err = AECreateDesc(typeNull, nil, 0, &theObjSpec);
                buffer = 'pURL';
                err = AECreateDesc(typeType,&buffer,4,&theSeldDesc);
                err = CreateObjSpecifier(desiredClass,&theSecondObjSpec,'prop',&theSeldDesc,true,&theObjSpec);      
                err = AEPutAttributeDesc(&theEvent,'csig',&theOptionalAttributeDesc);
                err = AEPutParamDesc(&theEvent,keyDirectObject, &theObjSpec);
            }
            if( noErr == err )
            {
                err = AESend( &theEvent, &reply, kAEWaitReply + kAENeverInteract, kAENormalPriority, 120, theIdleProc, NULL );
                if( noErr == err )
                {
                    gotReply = true;
                }
                else
                {
                    gotReply = false;                   
                }
                err = AEDisposeDesc(&theObjSpec);
                err = AEDisposeDesc(&theOptionalAttributeDesc);
                err = AEDisposeDesc(&theSeldDesc);
                err = AEDisposeDesc(&theSecondObjSpec);
                err = AEDisposeDesc(&theThirdObjSpec);
                err = AEDisposeDesc(&theFormDesc);
                err = AEDisposeDesc(&theNullDesc);              
            }
        }
        err = AEGetParamPtr(&reply, keyErrorNumber, typeLongInteger, NULL, &errNumber, sizeof(errNumber), &actualSize);
        if(true == gotReply )
        {
            err = AEGetParamDesc( &reply, keyDirectObject, typeAEList, &replyDesc );

            keyword = typeAEList;
            err = AEGetNthDesc( &replyDesc, 1, typeUnicodeText, &keyword, &replySingle);

            if( noErr == err)
            {
                OSStatus            status;
                Size                theSize;
                UnicodeMapping      iUnicodeMapping;
                UnicodeToTextInfo   theInfo;
                UniChar             theName[512];
                unsigned char       crapola[512]; // a.k.a. a pstring

                iUnicodeMapping.unicodeEncoding = kTextEncodingUnicodeDefault;
                iUnicodeMapping.otherEncoding = kTextEncodingMacRoman;
                iUnicodeMapping.mappingVersion = kUnicodeUseLatestMapping;
                status = CreateUnicodeToTextInfo(&iUnicodeMapping,&theInfo);                
                theSize = AEGetDescDataSize(&replySingle);

                err = AEGetDescData(&replySingle,&theName,512);
                if( noErr == err)
                {
                    err = ConvertFromUnicodeToPString(theInfo,theSize,theName,crapola);
                    if( noErr == err )
                    {
                        result = malloc( theSize * sizeof( char ));
                        if( NULL != result )
                        {
                            p2cstrcpy(result,crapola);
                            printf( "URL returned is %s\n", result);
                        }
                    }
                }
                status = DisposeUnicodeToTextInfo(&theInfo);
            }
        }
        err = AEDisposeDesc( &targetAddress );
        err = AEDisposeDesc( &replySingle );
        DisposeAEIdleUPP( theIdleProc );
    }
    if( NULL != retval )
        *retval = result;
    return(err);
}

我确信它不会编译,因为自 macOS 10.8 以来,许多 Carbon API 已经更新和重命名,但你明白了。

希望这篇长文能帮到你!


推荐阅读