Вызов javascript функций из C++ / Задание ReadyState у IHTMLDocument2

Тема в разделе "WASM.WIN32", создана пользователем fuckinff, 21 янв 2010.

  1. fuckinff

    fuckinff New Member

    Публикаций:
    0
    Регистрация:
    21 ноя 2009
    Сообщения:
    32
    Появилась необходимость использования функций JavaScript для обработких HMTL, пошарился по гуглу нашел несколько примеров и в итоге на данный момент имею следующие 2 метода:

    1-й метод: выполнение кода, передаваемого в ScriptCode, используя IHTMLDocument2->execScript
    Код (Text):
    1. HRESULT PExecScript (IHTMLDocument2 *pNewDoc, CHAR *ScriptCode, CHAR *ScriptLang)
    2. {
    3.     if (!pNewDoc || !ScriptCode || !ScriptLang) return E_FAIL;
    4.  
    5.     HRESULT hr = E_FAIL;
    6.  
    7.     IHTMLWindow2 *pWindow2 = NULL;
    8.  
    9.     if (SUCCEEDED(pNewDoc->get_parentWindow(&pWindow2)))
    10.     {
    11.         VARIANT pvarRet = {0};
    12.  
    13.         CComBSTR BstrLanguage(ScriptLang);
    14.         CComBSTR BstrFunction(ScriptCode);
    15.  
    16.         hr = pWindow2->execScript(BstrFunction, BstrLanguage, &pvarRet);
    17.  
    18.         VariantClear(&pvarRet);
    19.  
    20.         pWindow2->Release();
    21.     }
    22.  
    23.     return hr;
    24. }
    2-й метод: выполнение функции, находящейся в коде ХТМЛ, имя которой передается в FunctionName, используя IDispatch->Invoke
    Код (Text):
    1. HRESULT CallJScript (IHTMLDocument2 *pNewDoc, CHAR *FunctionName)
    2. {
    3.     if (!pNewDoc || !FunctionName) return E_FAIL;
    4.  
    5.     IDispatch *spScript;
    6.  
    7.     HRESULT hr = pNewDoc->get_Script(&spScript);
    8.     if(FAILED(hr)) return hr;
    9.  
    10.     BSTR bstrMember = A2WBSTR(FunctionName);
    11.     DISPID dispid = NULL;
    12.  
    13.     hr = spScript->GetIDsOfNames(IID_NULL,&bstrMember,1,LOCALE_SYSTEM_DEFAULT,&dispid);
    14.     SysFreeString(bstrMember);
    15.  
    16.     if(FAILED(hr)) return hr;
    17.  
    18.     DISPPARAMS dispparams = {0};
    19.     EXCEPINFO excepInfo = {0};
    20.     VARIANT vaResult;
    21.     UINT nArgErr = (UINT)-1;
    22.  
    23.     hr = spScript->Invoke(dispid,IID_NULL,0,DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr);
    24.  
    25.     VariantClear(&vaResult);
    26.  
    27.     return hr;
    28. }
    Оба метода прекрастно работают если получать интерфейс IHTMLDocument2 через IWebBrowser2 от инстанции браузера. Мне же необходимо обойтись без ненужных вызовов браузера + нужно чтобы на странице исполнялся только мой скрипт. Для получения интерфейса IHTMLDocument2 для любой строки использую приведенную ниже функцию (которая в свою очередь вызывает одну из вышеуказанных, в зависимости от параметров). Однако получая IHTMLDocument2 таким образом возникают следующие проблемы:
    1. функция HRESULT PExecScript() при выполнении базовых скриптов выдает странные результаты, к примеру если выполнить этот код:
    Код (Text):
    1. CHAR *Script = \
    2.     "var tr = document.body;" \
    3.     "if (tr) alert(tr.innerHTML); else alert('fail');";
    4.  
    5. BOOL bResult = ExecScript(File, Script, "javascript");
    то отображается какойто левый контент, не относящийся к тегу <body>, и подобных примеров куча! Нормально выполняются только простейшие функции.
    2. в HRESULT CallJScript() не получается интерфейс IDispatch *spScript, pNewDoc->get_Script(&spScript) выдает ошибку E_PENDING

    Кстати получая IHTMLDocument2 через ExecScript, ReadyState у полученного IHTMLDocument2 всегда равен unitialized! Видимо по этой причине возникает ошибка E_PENDING и вообще все проблемы.

    Исходя из этого, задаюсь следующими вопросами:
    - КАК изменить ReadyState у IHTMLDocument2 ?
    - КАК получить результат выполнения javascript-функции?


    Более подробно:
    1. Как получить IHTMLDocument2 так, чтобы ReadyState == complete? Возможно ли вручную изменить ReadyState?
    2. Если в качестве параметра функции PExecScript передавать document.write(), то скрипт функция вносит нужные изменения в код, однако нужные изменения невозможно получить от IHTML* интерфейсов! В принципе меня бы устроил этот подход, если бы можно было получить результат работы javascript-функции
    Код (Text):
    1. CHAR *Script = \
    2.     "document.write('<html><body><a id=32932 href=b>ok</a> body content</body></html');" \
    3.     "if (tr) alert(tr.innerHTML); else alert('fail');";
    3. Почемуто не работает IHTMLDocument2::write(), видимо опять-таки проблема кроется в ReadyState == unitialized

    Основная функция в контексте которой вызываются методы:
    Код (Text):
    1. BOOL ExecScript (CHAR *HTML, CHAR *ScriptCode, CHAR *ScriptLang, CHAR *FunctionName)
    2. {
    3.     if (!HTML)
    4.     {
    5.         return FALSE;
    6.     }
    7.  
    8.     HRESULT hr = E_FAIL;
    9.  
    10.     OLECHAR *oleHTML = (OLECHAR *) A2WBSTR(HTML);
    11.  
    12.     IHTMLDocument2 *pDoc = NULL;
    13.  
    14.     CoInitialize(NULL);
    15.  
    16.     if (SUCCEEDED(CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, IID_IHTMLDocument2, (LPVOID *) &pDoc)) && pDoc)
    17.     {
    18.         IPersistStreamInit *pPersist = NULL;
    19.  
    20.         if (SUCCEEDED(pDoc->QueryInterface(IID_IPersistStreamInit, (LPVOID *) &pPersist)) && pPersist)
    21.         {
    22.             IMarkupServices *pMS = NULL;
    23.  
    24.             pPersist->InitNew();
    25.             pPersist->Release();
    26.  
    27.             if (SUCCEEDED(pDoc->QueryInterface(IID_IMarkupServices, (LPVOID *) &pMS)) && pMS)
    28.             {
    29.                 IMarkupContainer *pMC = NULL;
    30.                 IMarkupPointer *pMkStart = NULL;
    31.                 IMarkupPointer *pMkFinish = NULL;
    32.  
    33.                 pMS->CreateMarkupPointer(&pMkStart);
    34.                 pMS->CreateMarkupPointer(&pMkFinish);
    35.  
    36.                 if (SUCCEEDED(pMS->ParseString(oleHTML, 0, &pMC, pMkStart, pMkFinish)) && pMC)
    37.                 {
    38.                     IHTMLDocument2 *pNewDoc = NULL;
    39.  
    40.                     if (SUCCEEDED(pMC->QueryInterface(IID_IHTMLDocument, (LPVOID *) &pNewDoc)) && pNewDoc)
    41.                     {
    42.                         if (FunctionName)
    43.                         {
    44.                             hr = CallJScript(pNewDoc, FunctionName);
    45.                         }
    46.                         else if (ScriptCode && ScriptLang)
    47.                         {
    48.                             hr = PExecScript(pNewDoc, ScriptCode, ScriptLang);
    49.                         }
    50.  
    51.                         pNewDoc->Release();
    52.                     }
    53.  
    54.                     pMC->Release();
    55.                 }
    56.  
    57.                 if (pMkStart)
    58.                     pMkStart->Release();
    59.  
    60.                 if (pMkFinish)
    61.                     pMkFinish->Release();
    62.  
    63.                 pMS->Release();
    64.             }
    65.         }
    66.  
    67.         pDoc->Release();
    68.     }
    69.  
    70.     CoUninitialize();
    71.  
    72.     SysFreeString((BSTR) oleHTML);
    73.  
    74.     return hr == S_OK;
    75. }
     
  2. Gnil

    Gnil New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2008
    Сообщения:
    41
    - КАК получить результат выполнения javascript-функции? - vaResult вот твой резалт, там правда могут быть VT_DISPATCH и т.п. хрень

    Использовал вызов по имени через dispatch. Для обратной связи можно также использовать external.

    Windowless HTMLDocument я пользовался, но там все сложнее чем у тебя в сниппете. В качестве базы использовался http://msdn.microsoft.com/en-us/library/bb508516(VS.85).aspx (reusing mshtml). По поводу работоспособности скриптов в windowless, не помню использовал ли их.
     
  3. fuckinff

    fuckinff New Member

    Публикаций:
    0
    Регистрация:
    21 ноя 2009
    Сообщения:
    32
    Уточню вопрос:

    - КАК получить результат выполнения javascript-функции, вызванной HRESULT PExecScript ()?
     
  4. fuckinff

    fuckinff New Member

    Публикаций:
    0
    Регистрация:
    21 ноя 2009
    Сообщения:
    32
    Осталась одна проблема: корректно загрузить хтмл в память! Т.к. приведенный мной пример при любых обсоятелствах при иницализации JS (execScript) сбрасывает хтмл до такого:
    Код (Text):
    1. <HEAD></HEAD>
    2. <BODY>
    3. <P>&nbsp;</P></BODY>
    возможно нужно перед загрузкой кода инициализировать js или чтото еще( прошу помочь кто чем может)

    этот вопрос решился!:
    - КАК получить результат выполнения javascript-функции, вызванной HRESULT PExecScript ()?

    вот таким вот образом (получаю обработанный хтмл код, в нем записан скрытый элемент div с результатами):
    Код (Text):
    1.         IHTMLDocument2 *Doc2Intf = NULL;
    2.         if (SUCCEEDED(pWindow2->get_document(&Doc2Intf)))
    3.         {
    4.             IHTMLDocument3 *Doc3Intf = NULL;
    5.  
    6.             if (SUCCEEDED(Doc2Intf->QueryInterface(IID_IHTMLDocument3, (VOID **) &Doc3Intf)) && Doc3Intf)
    7.             {
    8.                 IHTMLElement *DocElem = NULL;
    9.  
    10.                 if (SUCCEEDED(Doc3Intf->get_documentElement(&DocElem)) && DocElem)
    11.                 {
    12.                     BSTR InnerHTML;
    13.  
    14.                     if (SUCCEEDED(DocElem->get_innerHTML(&InnerHTML)))
    15.                     {
    16.                         if (ExecutionResult) *ExecutionResult = WCharToAnsi((WCHAR *) InnerHTML);
    17.                     }
    18.  
    19.                     DocElem->Release();
    20.                 }
    21.  
    22.                 Doc3Intf->Release();
    23.             }
    24.  
    25.             Doc2Intf->Release();
    26.         }
     
  5. Gnil

    Gnil New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2008
    Сообщения:
    41
    Твой способ получения резалта от функции слишком сложный. external выглядит проще.

    Загрузка делается так
    http://msdn.microsoft.com/en-us/library/aa752047%28VS.85%29.aspx( persist stream + CreateStreamOnHglobal)
     
  6. fuckinff

    fuckinff New Member

    Публикаций:
    0
    Регистрация:
    21 ноя 2009
    Сообщения:
    32
    Спасиб но это не помогает( Решил пойти другим путем и использовать windows scripting host