从第三个应用程序获取浏览器 URL
Get browser URL from third application
我正在开发 MacOS 应用程序,我想知道是否有办法从中获取 url 活动浏览器。应用程序在 C++ 中完成。
我想在不使用 AppleScript 的情况下获取它。
这可能吗?
谢谢
这不是一个简单的答案,但好消息是 "yes, it's possible to do this without having to use AppleScript",坏消息是 "you'll have to use AppleScript to begin with"。
让我详细说明一下:浏览器应用程序通常有一个 Applescript 字典(您可以使用 /Applications/Utilities
文件夹中的 "Script Editor" 应用程序查看它。下面是字典的样子 Google Chrome:
你会看到我找到了 "tab" class 并且在那里你会看到 URL 属性.
所以你需要做的是首先编写一个 AppleScript 来为你的浏览器获取 windows重新定位。然后,当它起作用时,您需要将 AppleScript 转换为底层的原始 AppleEvents(这是 AppleScripts 编译成的)。 AppleScripts 和 AppleEvents 都可以用代码完成。
据我所知,您真正想要的答案(例如"can I use some top secret API or send a URL from my code into some open port on my local machine to query the browser?")并不存在。 AppleScript 和 AppleEvents 是 Apple 提供的用于自动化大多数应用程序的长期方式,您需要利用它们。
如果您决定继续使用 AppleScript 并可能将它们转换为 AppleEvents,这就是我要做的。
1) 查找公开可用的 AppleScript 来完成您想要的工作。
2) 将 AppleScript 转换为 AppleEvents
3) 编写 AppleEvents。
对于 1,这是一个在 Safari 中遍历每个 window 的脚本,我从 this tutorial:
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 步,我认为您可以使用 the Accessory View Pane in Script Editor 查看原始事件和产生的结果。
对于第 3 步,我用 C 语言编写的这段古老代码将为给定 window ID 的浏览器 window 获取 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);
}
我确定它不会编译,since a number of Carbon API's have been updated and renamed 从 macOS 10.8 开始,但你明白了。
希望这篇长文能帮到你!
我正在开发 MacOS 应用程序,我想知道是否有办法从中获取 url 活动浏览器。应用程序在 C++ 中完成。
我想在不使用 AppleScript 的情况下获取它。
这可能吗?
谢谢
这不是一个简单的答案,但好消息是 "yes, it's possible to do this without having to use AppleScript",坏消息是 "you'll have to use AppleScript to begin with"。
让我详细说明一下:浏览器应用程序通常有一个 Applescript 字典(您可以使用 /Applications/Utilities
文件夹中的 "Script Editor" 应用程序查看它。下面是字典的样子 Google Chrome:
所以你需要做的是首先编写一个 AppleScript 来为你的浏览器获取 windows重新定位。然后,当它起作用时,您需要将 AppleScript 转换为底层的原始 AppleEvents(这是 AppleScripts 编译成的)。 AppleScripts 和 AppleEvents 都可以用代码完成。
据我所知,您真正想要的答案(例如"can I use some top secret API or send a URL from my code into some open port on my local machine to query the browser?")并不存在。 AppleScript 和 AppleEvents 是 Apple 提供的用于自动化大多数应用程序的长期方式,您需要利用它们。
如果您决定继续使用 AppleScript 并可能将它们转换为 AppleEvents,这就是我要做的。
1) 查找公开可用的 AppleScript 来完成您想要的工作。
2) 将 AppleScript 转换为 AppleEvents
3) 编写 AppleEvents。
对于 1,这是一个在 Safari 中遍历每个 window 的脚本,我从 this tutorial:
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 步,我认为您可以使用 the Accessory View Pane in Script Editor 查看原始事件和产生的结果。
对于第 3 步,我用 C 语言编写的这段古老代码将为给定 window ID 的浏览器 window 获取 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);
}
我确定它不会编译,since a number of Carbon API's have been updated and renamed 从 macOS 10.8 开始,但你明白了。
希望这篇长文能帮到你!