Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: COM/DCOM/ActiveX/ATL/CORBA > Передача объекта Array в COM - метод


Автор: log584 22.12.2010, 20:56
Есть JScript - скрипт, запускаемый под IIS 5.0. В скрипте создается объект,  реализуемый в проекте ATL на VSC++ 2008.
Код

    var MyForm = Server.CreateObject("CMYASP.HTMLForm");
    var arr = new Array(23, 14, 35, 67);
    MyForm.Items = arr;

Как видно, созданный в скрипте объект типа Array присваивается свойству. Дальше.
Ловлю данный объект на стороне сервера:
Код

STDMETHODIMP CHTMLForm::put_Items(VARIANT * vParam)
{
    IDispatch * pDisp = vParam->pdispVal;
    return( S_OK );
}

vParam->vt = 9 = VT_DISPATCH.
А вот что это за IDispatch конкретно я не знаю. Соответственно и работать с ним далее не получается.
В этом собственно, и весь вопрос. Как узнать что это за тип, что бы знать его методы. В инете информации о интерфейсах объекта JScript Array я не нашел, пролазил два дня. Помогите пожалуйста.

Автор: jonie 23.12.2010, 09:45
Код

// In *.idl file 
[propget, id(0)] HRESULT ArrayProperty([out, retval] SAFEARRAY(VARIANT) *pArray);
[propput, id(0)] HRESULT ArrayProperty([in] SAFEARRAY(VARIANT) Array);


// Somewhere in javascript

function ax2js(axArray) {    
    return new VBArray(array).toArray();
}

function js2ax(jsArray) {
    var dict = new ActiveXObject("Scripting.Dictionary");

    for (var i = 0; i < jsArray.length; i++) {
        dict.add(i, jsArray[i]);
    }

    return dict.Items();
}

function fooHandler() {
    var ax = new ActiveXObject("My.My");

    var ar = ax2js(ax.ArrayProperty);

    ax.ArrayProperty = js2ax(ar);
}



© отсюда: http://stackoverflow.com/questions/1926723/passing-array-of-bytes-from-activex-to-javascript-and-vice-versa/4345682#4345682

Автор: xvr 23.12.2010, 10:58
Цитата(log584 @  22.12.2010,  20:56 Найти цитируемый пост)
Как узнать что это за тип, что бы знать его методы.
Array вестимо  smile 

Цитата(log584 @  22.12.2010,  20:56 Найти цитируемый пост)
В инете информации о интерфейсах объекта JScript Array я не нашел, пролазил два дня. Помогите пожалуйста. 

Помогаю - http://msdn.microsoft.com/en-us/library/k4h76zbx%28v=VS.85%29.aspx
А индексировать его можно попробовать через DISPID_VALUE (с параметром)

Добавлено через 11 минут и 35 секунд
Update - данные, помещенные в массив становятся доступны по имени, совпадающим с числом:
Код

1> a=([1,2,3]);
1,2,3
1> Info(a)
DISPATCH: 1,2,3

Supported interfaces:
IUnknown
IDispatch
IDispatchEx

Supported methods/properties (IDispatchEx):
 0
 1
 2
 valueOf
 toString
 length
1> a[5]=10
10
1> Info(a)
DISPATCH: 1,2,3,,,10

Supported interfaces:
IUnknown
IDispatch
IDispatchEx

Supported methods/properties (IDispatchEx):
 0
 1
 2
 valueOf
 toString
 length
 5
1>


Автор: log584 23.12.2010, 18:59
Цитата

Array вестимо

Да понятно, что не FileSystemObject. С объектом JScript Array я уже давно ознакомился в MSDN на локальной машине. xvr, на странице, в которую ты указал, описывается именно объект, используемый на стороне клиента автоматизации, а вот какой интерфейс использовать для доступа к методам кокласса данного объекта, на стороне C++, реализации COM сервера? Вот, может быть я изначально неточно сформулировал вопрос, извините.

jonie, по - моему, года два назад у меня уже была подобная ситуация ( опять - же по - моему с помощью VBArray я и выкручивался), ладно, я попробую передавать COM  - серверу объект VBArray, должно получиться, но все равно - это изврат. Изначально вопрос стоял о том, что за интерфейс реализован коклассом объекта Array, передаваемого из JScript на сторону C++? С тем, чтобы посмотреть его документацию и использовать его (на стороне C++ COM - сервера). Вот это будет самый верный путь.

Автор: xvr 23.12.2010, 20:31
Цитата(log584 @  23.12.2010,  18:59 Найти цитируемый пост)
а вот какой интерфейс использовать для доступа к методам кокласса данного объекта, на стороне C++, реализации COM сервера? 
Тот же самый. IDispatch с методами и пропертями, которые описанны на странице из MSDN.
Полный список реализуемых интерфейсов был на строках 7-9 в листинге моем сообщении

Цитата(log584 @  23.12.2010,  18:59 Найти цитируемый пост)
 попробую передавать COM  - серверу объект VBArray, 
Угу. К сожалению стандартный метод передачи массивов в ActiveX (SAFEARRAY) интерпретатор JS не поддерживает  smile 

Цитата(log584 @  23.12.2010,  18:59 Найти цитируемый пост)
Изначально вопрос стоял о том, что за интерфейс реализован коклассом объекта Array, передаваемого из JScript на сторону C++?
Все, что выходит из стандартных MS интерпретаторов не имеет какого то специального типа/интерфейса - они все обычные IDispatch (точнее IDispatchEx)


Автор: log584 23.12.2010, 23:56
Цитата

Тот же самый

Тот же самый - это какой? (Какой интерфейс для C++?) Еще раз вопрос конкретно, какой интерфейс, его наименование, заголовочный файл из MSDN для C++? Ну пускай не из MSDN...
Цитата

IDispatch с методами и пропертями, которые описанны на странице из MSDN.

Какие такие методы и пропертя мне дают IUnknown, IDispatch (или интерфейсы классов), чтобы я смог вызывать конкретные методы кокласса для данного интерфейса объека Array?

Да есть у меня IDispatch, который ни о чем еще не гововорит. IUnknown, IDispatch сами по себе не предназначены для получения информации о методах интерфейса. Да, еще, 
не реализован у моего объекта JScript Array, передаваемого на сторону C++ сервера интерфейс IDispatchEx!!! У объекта, переданного на сторону сервера, при попытке инстанциировать интерфейс типа IDispatchEx в HRESULT возвращается E_NOINTERFACE. ???????? И еще, скорей всего, как я понял Делфинарий?

Цитата

Угу. К сожалению стандартный метод передачи массивов в ActiveX (SAFEARRAY) интерпретатор JS не поддерживает

А вот это ценная информация. И все равно, что за объект я получаю на стороне сервера?

Может полный проект дать?

Автор: xvr 24.12.2010, 08:06
Цитата(log584 @  23.12.2010,  23:56 Найти цитируемый пост)
Какие такие методы и пропертя мне дают IUnknown, IDispatch (или интерфейсы классов), чтобы я смог вызывать конкретные методы кокласса для данного интерфейса объека Array?
IDispatch::GetIDSOfNames & IDispatch::Invoke

Цитата(log584 @  23.12.2010,  23:56 Найти цитируемый пост)
IDispatch сами по себе не предназначены для получения информации о методах интерфейса.
Ничего не путаете? IDispatch был сделан ИМЕННО ДЛЯ ЭТОГО!

Цитата(log584 @  23.12.2010,  23:56 Найти цитируемый пост)
У объекта, переданного на сторону сервера,
Вы хотели сказать - клиенту на С++? Т.к. сервер находится как раз внутри JS интерпретатора

Цитата(log584 @  23.12.2010,  23:56 Найти цитируемый пост)
при попытке инстанциировать интерфейс типа IDispatchEx в HRESULT возвращается E_NOINTERFACE.
Значит как то не так просили. Надо через QueryInterface


Цитата(log584 @  23.12.2010,  23:56 Найти цитируемый пост)
И все равно, что за объект я получаю на стороне сервера?
IDispatch. НЕ DUAL, ПРОСТОЙ IDispatch.


Цитата(log584 @  23.12.2010,  23:56 Найти цитируемый пост)
Может полный проект дать?

Вот, кусок из реального проекта:
Код

static void invoke_aux(IDispatch* dsp, wchar_t* name, WORD flags, Variant &v)
{
 DISPID disp;
 if (dsp->GetIDsOfNames(IID_NULL,&name,1,LOCALE_SYSTEM_DEFAULT,&disp)<0)
  {
   if (flags&DISPATCH_PROPERTYPUT) throw Exception("JS: Can't set property '"+AnsiString(name)+"' for aux object");
   v.Clear();
   return;
  }
 DISPPARAMS dparams;
 dparams.rgdispidNamedArgs=NULL;
 dparams.cNamedArgs=0;
 VARIANT* rv;
 if (flags&DISPATCH_PROPERTYPUT)
  {
   dparams.rgvarg=(VARIANT*)&v;
   dparams.cArgs=1;
   rv=NULL;
  }
 else
  {
   dparams.rgvarg=NULL;
   dparams.cArgs=0;
   rv=(VARIANT*)&v;
  }
 UINT err;
 if (dsp->Invoke(disp,IID_NULL,LOCALE_SYSTEM_DEFAULT,flags,&dparams,rv,NULL,&err)<0)
  throw Exception("JS: IDIspatch::Invoke falure on aux object (property '"+AnsiString(name)+"')");
}
Вызов из поданного IDispatch (dsp) метода/проперти по имени (name). Результат в v
В качестве имени используйте строку с индексом, (для индексации индексом 5 делайте так):
Код

Variant val;
invoke_aux(dsp,L"5", DISPATCH_PROPERTYGET,val);


Автор: log584 24.12.2010, 18:44
Спасибо, дошло. Но все равно это не решение. Какой объект, в таком случае я должен создать на стороне C++ сервера, чтобы он, попав на сторону скрипта, там в скрипте имел тип Array?

Автор: xvr 24.12.2010, 21:10
Цитата(log584 @ 24.12.2010,  18:44)
Какой объект, в таком случае я должен создать на стороне C++ сервера, чтобы он, попав на сторону скрипта, там в скрипте имел тип Array?

В JS все объекты равноправны. А Array - это всего лишь один из встроенных объектов. Так что можно сделать IDispatch, который будет реализовывать методы Array, такой объект будет практически неотличим от родного JS Array'я. Полнофункциональный JS Array можно создать только внутри самого интерпретатора JS. Например попросив интерпретатор вычислить выражение  new Array() или просто ([]), в результате будет возвращен VARIANT, содержащий IDispatch настоящего JS Array'я. А дальше его можно наполнять данными (через IDispatchEx::GetDispID c fdexNameEnsure)


Автор: log584 24.12.2010, 22:15
Ладно, с этим разобрался. Спасибо большое за терпение. Если появятся дополнительные, надеюсь можно будет обратиться за помощью?

Автор: xvr 25.12.2010, 13:32
Цитата(log584 @  24.12.2010,  22:15 Найти цитируемый пост)
Если появятся дополнительные, надеюсь можно будет обратиться за помощью?

Конечно

Автор: log584 25.12.2010, 21:57
Опять проблема. Если я правильно понял, то динамический создать элементы объекта массива на стороне COM - метода в виде индексированных свойств объекта можно только с помощью IDispatchEx. Если так, то нужно знать CLSID кокласса, реализующего IDispatchEx. А какой такой есть кокласс, задача которого реализовывать методы интерфейса IDispatchEx и больше ничего.
Или же искать любой другой CLSID, который реализует IDispatchEx. Но так не рационально вообще. В инете опять же я что - то через яндекс инфу не нашел. При попытке создать объект с интерфейсом IDispatchEx в возвращаемом HRESULT - 0x80040154 "Класс не зарегистрирован". Так как же мне все - таки его создать? Ведь основная идея в том, что бы создать обект массива в COM - методе, который будет передан JScript  скрипту.
Данный вопрос поднимался на http://www.rsdn.ru/forum/com/1164089.all.aspx, а как практически это сделать никто не указал.

Автор: xvr 27.12.2010, 21:44
Цитата(log584 @  25.12.2010,  21:57 Найти цитируемый пост)
Опять проблема. Если я правильно понял, то динамический создать элементы объекта массива на стороне COM - метода в виде индексированных свойств объекта можно только с помощью IDispatchEx. Если так, то нужно знать CLSID кокласса, реализующего IDispatchEx. А какой такой есть кокласс, задача которого реализовывать методы интерфейса IDispatchEx и больше ничего.
Не понял - вам нужно получить IDispatchEx или создать? Если первое, то банальный IDispatch::QueryInterface(IID_DispatchEx,...). Если второе - то либо поручить это JS интерпретатору (через IActiveScriptParse::ParseScriptText("new Array()") ), либо имплементировать самому (с нуля)



Автор: log584 28.12.2010, 17:13
Вот код:
Код

STDMETHODIMP CHTMLForm::get_Items(VARIANT * vItems)
{
    HRESULT hr;
    CComPtr<IActiveScript> pActScp;
    IActiveScriptParse * pActScpPrs;

    hr = pActScp.CoCreateInstance(__uuidof(CLSID_JScript));//S_OK
    if(FAILED(hr)){
        vItems->vt = VT_EMPTY;
        return( S_OK );
    }

    hr = pActScp->QueryInterface(__uuidof(IActiveScriptParse), (void **)&pActScpPrs);//S_OK
    if(FAILED(hr)){
        vItems->vt = VT_EMPTY;
        return( S_OK );
    }

    hr = pActScpPrs->InitNew();//S_OK

    VARIANT vResult;
    EXCEPINFO excinf = {0};

    //E_UNEXPECTED
    hr = pActScpPrs->ParseScriptText(OLESTR("new Array();"), NULL, NULL, NULL, NULL, NULL, SCRIPTTEXT_ISVISIBLE, &vResult, &excinf);//E_UNEXPECTED

    return( hr );
}

Что нет может быть так? Обязательно ли реализовывать IActiveScriptSite?
Код

    IActiveScript::SetScriptSite(IActiveScriptSite *pScriptSite);

Автор: xvr 28.12.2010, 22:41
Цитата(log584 @  28.12.2010,  17:13 Найти цитируемый пост)
Что нет может быть так?
Интерпретатор не переведен в запущенное состояние (IActiveScript::SetScriptState(SCRIPTSTATE_STARTED))
Цитата(log584 @  28.12.2010,  17:13 Найти цитируемый пост)
Обязательно ли реализовывать IActiveScriptSite?
Обязательно.
Вот код инициализации JS интерпретатора (выдрано из рабочего проекта):
Код

void ObjectsHolder::find_js_engine()
{
 USES_CONVERSION;
 HKEY  key;
 DWORD sz=1024, type;
 HRESULT hr;
 char buf[1024];
 CLSID js_clsid;

 if (RegOpenKeyEx(HKEY_CLASSES_ROOT,"JavaScript\\CLSID",0,KEY_READ,&key)!=ERROR_SUCCESS)
  throw Exception("JS: Can't open JavaScript key in Registry");
 if (RegQueryValueEx(key,"",NULL,&type,(unsigned char*)buf,&sz)!=ERROR_SUCCESS)
  {RegCloseKey(key); throw Exception("JS: Can't get JavaScript key from Registry");}
 RegCloseKey(key);

 hr=CLSIDFromString(A2W(buf),&js_clsid);
 if (!SUCCEEDED(hr))
  throw Exception("JS: Invalid format of CLSID of JavaScript Script Engine");

 hr=CoCreateInstance(js_clsid,0,CLSCTX_INPROC_SERVER,IID_IActiveScript,(void**)&js_engine);
 if (!SUCCEEDED(hr))
  throw Exception("JS: Can't create JavaScript Scripting Engine");
 hr=js_engine->SetScriptSite((IActiveScriptSite*)this);
 if (!SUCCEEDED(hr)) throw Exception("JS: Can't set ScriptSite");
 {
  CComQIPtr<IActiveScriptParse,&IID_IActiveScriptParse> asp(js_engine);
  if (!asp) throw Exception("JS: Can't get interface to initialize JS engine");
  asp->InitNew();
 }
 hr=js_engine->SetScriptState(SCRIPTSTATE_STARTED);
 if (!SUCCEEDED(hr))
  throw Exception("JS: Can't start Script Engine");
}


По поводу ParseScriptText - в 7 параметр надо передать SCRIPTTEXT_ISEXPRESSION (SCRIPTTEXT_ISVISIBLE там необязателен)

Вот код для исполнения произвольной JS строки (Builder)
Код

HRESULT ObjectsHolder::js_execute(AnsiString script, Variant* rv)
{USES_CONVERSION;
 EXCEPINFO ex;
 if (script.IsEmpty()) return S_FALSE;
 CComQIPtr<IActiveScriptParse,&IID_IActiveScriptParse> asp(js_engine);
 if (!asp) throw Exception("JS: Can't get interface to load script code");  
 return asp->ParseScriptText(A2W(script.c_str()),NULL,NULL,NULL,0,0,rv?SCRIPTTEXT_ISEXPRESSION:0,(VARIANT*)rv,&ex);
}


Автор: log584 3.1.2011, 13:19
Поздравляю всех с наступившим!
Все что задумал получилось, спасибо.

Автор: nikolaynnov 19.12.2012, 17:50
Цитата(jonie @  23.12.2010,  09:45 Найти цитируемый пост)

Код

function ax2js(axArray)
{
    return new VBArray(array).toArray();
}



Может глупый вопрос: зачем "new VBArray"?

Вроде работает просто: 
Код

function ax2js(axArray)
{
   return axArray.toArray();
}



Тогда вообще ax2js можно не использовать, а сразу писать:
Код

var ar = ax.ArrayProperty.toArray();


Или есть какие-то подводные камни, и надо всегда использовать new VBArray()?

Автор: xvr 19.12.2012, 20:35
Цитата(nikolaynnov @  19.12.2012,  17:50 Найти цитируемый пост)
Может глупый вопрос: зачем "new VBArray"?
Для того, что бы из SAFEARRAY сделать легальный JS объект (VBArray). Сам по себе SAFEARRAY в JS никак не может быть использован

Цитата(nikolaynnov @  19.12.2012,  17:50 Найти цитируемый пост)
Вроде работает просто: 

Именно для SAFEARRAY'ев?

Автор: nikolaynnov 20.12.2012, 10:48
Цитата(xvr @  19.12.2012,  20:35 Найти цитируемый пост)
Именно для SAFEARRAY'ев?


Именно. Вот более подробный пример:

В idl:
Код

    [propget, id(8), helpstring("property cameras")] HRESULT cameras([out, retval] SAFEARRAY(VARIANT) *pArray);
    [propput, id(8), helpstring("property cameras")] HRESULT cameras([in] SAFEARRAY(VARIANT) Array);


В cpp:
Код

STDMETHODIMP CFtpClient::get_cameras( SAFEARRAY** pVal )
{
    if(!pVal)
        return E_INVALIDARG;

    CComSafeArray<VARIANT> bsVal;

    for (std::set<CString>::const_iterator it = m_cameraIds.begin(); it != m_cameraIds.end(); ++it)
    {
        CComVariant str = *it;
        if (FAILED(bsVal.Add(str)))
            Log(TEXT("CComSafeArray failed"));
    }
    *pVal = bsVal.Detach();
    return S_OK;
}


И в скрипте:
Код

    var cameras = myObj.cameras.toArray(); 
    if (!cameras.length)
        return; 
    ...
    for (var i = 0; i < cameras.length; i++)
    {
        var cam = cameras[i];
        ...
    } 

Автор: xvr 20.12.2012, 13:24
Значит доделали функциональность с какой то версии JS интерпретатора. Раньше такое не проходило (вроде)

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)