Получить URL браузера из третьего приложения

Я работаю над приложением MacOS и хотел бы узнать, есть ли способ получить из него URL-адрес активного браузера. Приложение сделано в C ++.

Я хотел бы получить его без использования AppleScript.

Это возможно?

Спасибо

0

Решение

Это не простой ответ, но хорошая новость заключается в том, что «да, это возможно сделать без необходимости использования 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, я думаю, вы можете использовать панель просмотра аксессуаров в редакторе сценариев чтобы увидеть необработанные события и результаты.

На третьем этапе этот древний код, который я написал на C, получит 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);
}

Я уверен, что это не скомпилируется, так как ряд Carbon API были обновлены и переименованы начиная с macOS 10.8, но вы поняли.

Надеюсь, это длинное эссе поможет вам!

1

Другие решения

Других решений пока нет …