Тема: COM передать параметр по ссылке

Пишу интеграцию с Компас.11, работаю с ним через его АПИ.

Если функции возвращают LPDISPATCH, то пишу следующим образом:

'LPDISPATCH GetParamStruct (short structType);
Set iTextLineParam = iKompasObject.GetParamStruct(29)

Все работает. Но есть функция, возвращающая тот же тип результата, но вызывающая ошибку:

'LPDISPATCH ksGetStampColumnText (long* numb);
Set iTextLineDynamicArray = iStamp.GetStampColumnText( numb )     
'ОШИБКА - Объект не поддерживает это свойство или метод iStamp.GetStampColumnText

Есть подозрение, что параметр надо передавать не по значению, а по ссылке.
Как это сделать? Преобразование VarPtr(numb) - не работает

Полный текст:

Sub FormMacro_COMTEST(TCSActiveModule) ' Проверка COM 

    kStarted = 0      
             
    'открываемый файл
    fileName = "C:\Program Files\CSoft\TechnologiCS\TEMP\46.19-1.cdw"
          
    'открыть компас
    On Error Resume Next                                                              
    Set iKompasObject = GetObject(,"KOMPAS.Application.5")  'это если уже запущен
    If Err Then                                                   
        Set iKompasObject = CreateObject("KOMPAS.Application.5") 
        kStarted = 1
        Err.Clear
    End If
    On Error goto 0
    
                    
    iKompasObject.Visible = True ' показать компас 


    'открыть выгруженный файл
    Set d2d = iKompasObject.Document2D 
    res = d2d.ksOpenDocument( fileName, False ) 
                                                 
    
    Set parStruct = iKompasObject.GetParamStruct(35)
        
    'Получить интерфейс ksStamp основной надписи документа.
    Set iStamp = d2d.GetStamp()   
    
    'Открыть основную надпись.      
    iStamp.ksOpenStamp() 
    
        
    numb = 2   
    'long ksColumnNumber (long numb); //Определить номер ячейки основной надписи 
    iStamp.ksColumnNumber numb 
                                      
    'iTextLineParam:=ksTextLineParam(iKompas.GetParamStruct(ko_TextLineParam));
    'iTextItemParam:=ksTextItemParam(iKompas.GetParamStruct(ko_TextItemParam));    
    Set iTextLineParam = iKompasObject.GetParamStruct(29)
    Set iTextItemParam = iKompasObject.GetParamStruct(31)       
    'LPDISPATCH ksGetStampColumnText (long* numb);
    'LPDISPATCH GetParamStruct (short structType);
    
    'iTextLineDynamicArray:=ksDynamicArray(iStamp.ksGetStampColumnText(numb));
    'iTextLineDynamicArray.ksGetArrayItem(0, iTextLineParam);
    'iTextItemDynamicArray:=ksDynamicArray(iTextLineParam.GetTextItemArr());
    'iTextItemDynamicArray.ksGetArrayItem(0, iTextItemParam);   
                
    'Получить текст ячейки основной надписи.
    'LPDISPATCH ksGetStampColumnText (long* numb);
    Set iTextLineDynamicArray = iStamp.GetStampColumnText( numb )     
    'ОШИБКА - Объект не поддерживает это свойство или метод iStamp.GetStampColumnText
    
  
    'Закрыть основную надпись.
    Istamp.ksCloseStamp()          
    
    If kStarted = 1 Then iKompasObject.Quit 'закрытие компаса   

End Sub    

Re: COM передать параметр по ссылке

Ну, мы пишем аналогичный проект, но под Компас V12 и на Delphi. При получении значения вышеуказанной функции используем приведение к типу ksDynamicArray, примерно так:

TextLineDynamicArray := ksDynamicArray(Stamp.ksGetStampColumnText(numb));

Re: COM передать параметр по ссылке

Дмитрий Гамий пишет:

но под Компас V12 и на Delphi.

Жаль, надо именно под TCS. На сколько мне известно, в Delphi нет разницы при передаче параметров. Он будет взят так, как описана функция. А в си можно написать &имя_переменной.
Неизвестно как привести к типу в VB TCS да и нужно ли?

Re: COM передать параметр по ссылке

Неизвестно как привести к типу в VB TCS да и нужно ли?

Ну, насколько я знаю, VB сам приводит всё, что нужно.

Ну и ещё - а не нужно ли проверять возвращаемое значение функции

    'Открыть основную надпись.      
    iStamp.ksOpenStamp()

Может, в этом  месте происходит ошибка, а потом iStamp.GetStampColumnText( numb )  из-за этого не работает?
В демо-примерах к Компас SDK для Visual Basic обычно так делается:

Set iStamp = doc.GetStamp()
If Not iStamp Is Nothing And iStamp.ksOpenStamp Then
'
'
End If

Re: COM передать параметр по ссылке

Все верно, iStamp.ksOpenStamp = 0, то есть - не открыл (
Буду разбираться дальше

Re: COM передать параметр по ссылке

Теперь штамп открывается.
Могу писать в штамп через ksTextLine, могу его очищать через ksClearStamp(0),
но при выполнении ksGetStampColumnText( numb ) выходит ошибка "Несоответствие типа"
хотя numb = CLng(2) и имеет требуемый тип Long.

Что еще может быть не так?

Re: COM передать параметр по ссылке

dispinterface ksStamp {
        properties:
            [id(0x00000001), helpstring("Указатель на штамп.")            
]
            long reference;
        methods:
            [id(0x00000002), helpstring("Открыть составной объект  - штамп.")]
            long ksOpenStamp();
            [id(0x00000003), helpstring("Закрыть составной объект  - штамп.")]
            long ksCloseStamp();
            [id(0x00000004), helpstring("Очистить составной объект - штамп.")]
            long ksClearStamp(long numb);
            [id(0x00000005), helpstring("Выдать текст графы.")]
            IDispatch* ksGetStampColumnText(long* numb);
            [id(0x00000006), helpstring("Заменить текст графы.")]
            long ksSetStampColumnText(
                            long numb, 
                            IDispatch* textArr);
            [id(0x00000007), helpstring("Определить номер ячейки.")]
            long ksColumnNumber(long numb);
            [id(0x00000008), helpstring("Создать компоненту строки текста.")]
            long ksTextLine(IDispatch* textItem);
    };

К сожалению не в курсе темы, но все же почему

Set iTextLineDynamicArray = iStamp.GetStampColumnText( numb )     

а не как видно по всем текстам и хелпам

Set iTextLineDynamicArray = iStamp.ksGetStampColumnText( numb )     

(изменено: Bolshakov_AV, 15 декабря 2010 11:26:14)

Re: COM передать параметр по ссылке

В общем, сейчас имеем следующий текст:

Sub FormMacro_COMTEST(TCSActiveModule) ' Проверка COM 

    kStarted = 0      
    
    fileName = "D:\Bolshakov_AV\файло компасовое\Сопло\16.2-9.cdw"
    
    On Error Resume Next                                                              
    Set iKompasObject = GetObject(,"KOMPAS.Application.5")  'это если уже запущен
    If Err Then                                                   
        Set iKompasObject = CreateObject("KOMPAS.Application.5") 
        kStarted = 1
        Err.Clear
    End If
    On Error goto 0
                    
    iKompasObject.Visible = True ' показать компас 

    'открыть выгруженный файл
    Set Doc = iKompasObject.Document2D 
    res = Doc.ksOpenDocument( fileName, False ) 
    
    If ((res=True) And (Doc.reference<>0) And (Not Doc Is Nothing)) Then
                           
        Set stamp = Doc.GetStamp()
                                  
        If (Not stamp Is Nothing) Then
           
            If (stamp.ksOpenStamp() = 1) Then
              
                stamp.ksColumnNumber(2)
                
                'stamp.ksClearStamp(0) 'работает
                 
                'так и не работает
                numb = CInt(2)         
                Set IStamp = stamp.ksGetStampColumnText( numb ) 
                
'далее все работает         
                ' Интерфейс параметров компоненты строки текста
                Set itemParam = iKompasObject.GetParamStruct(31) 'ko_TextItemParam 
                
                If Not itemParam Is Nothing Then        ' Интерфейс создан
                    itemParam.Init                        ' Инициализация
                           
                    Set itemFont = itemParam.GetItemFont  ' Интерфейс параметров шрифта компоненты строки текста
                        
                    If Not itemFont Is Nothing Then       ' Интерфейс создан
                        itemFont.Init                       ' Инициализация
                        itemFont.SetBitVectorValue NEW_LINE, True ' Новая строка
                        itemParam.s = "1111111"             ' Текст компоненты строки текста
                        Doc.ksTextLine itemParam            ' Добавить строку в ячейку основной надписи
                          
                        Set itemFont = Nothing
                    End If
                    
                    Set itemParam = Nothing

                End If
                
                stamp.ksCloseStamp()
            
            End If
        
        End If
         
        Doc.ksSaveDocument( fileName )
        Doc.ksCloseDocument()                                         
   
    End If
    
    
    If kStarted = 1 Then iKompasObject.Quit 'закрытие компаса   

End Sub    

В общем пробовал и так и эдак:

LPDISPATCH ksGetStampColumnText (long* numb);
reference GetStampColumnText (unsigned int *numb);

В любом случае параметр передается по ссылке и в нем же возвращается (!) результат.
В интернете нашел упоминание об этой проблеме в VBS и не один раз. Но решения не нашел.

http://www.rsdn.ru/forum/com/80999.flat.aspx
Тут народ считает, что параметры все равно передаются по значению, за исключением элементов массива.

http://forum.script-coding.info/viewtopic.php?pid=33028
тут народ создает "CreateObject("htmlfile")" и его выполняет на другом языке.

вот еще интересный ответ:
http://stackoverflow.com/questions/9667 … f-to-com-c

Re: COM передать параметр по ссылке

насколько я знаю в VBScript значения по умолчанию передаются по ссылке, но можно указать явно передачу по значению.

В JSScrpit все значение передаются по значению (это как раз проблема 1-кого топика), а во втором проблема как вернуть несколько значений.

Так что в данном смысле проблемы не у VBS.

По собщению что я написал ошибочка. В начале у вас сообщение другое было просто.

"Несоответствие типа" говорит о том что метод нашелся, но где то проблема с параметрами.

а tlb-бу можете прицепить  к теме чтобы глянуть оригинальную?

(изменено: Bolshakov_AV, 15 декабря 2010 11:39:48)

Re: COM передать параметр по ссылке

Это все, что есть

Re: COM передать параметр по ссылке

ну вроде

Set iTextLineDynamicArray = iStamp.ksGetStampColumnText( numb )

совершенно правильно написано.

Попробуйте как вариант (что получится)

Set iTextLineDynamicArray = iStamp.ksGetStampColumnText( 1 )
Call iStamp.ksGetStampColumnText( 1 )
Call iStamp.ksGetStampColumnText( numb )

(изменено: Bolshakov_AV, 15 декабря 2010 13:30:56)

Re: COM передать параметр по ссылке

...
Dim numb
                
numb=CLng(1)      
               
Set IStamp = stamp.ksGetStampColumnText( 1 )  
Set IStamp = stamp.ksGetStampColumnText( numb )  
Call stamp.ksGetStampColumnText( 1 ) 
Call stamp.ksGetStampColumnText( numb )
stamp.ksGetStampColumnText 1  
stamp.ksGetStampColumnText numb
...

Все 6 способов выдают результат:
Error text: Ошибка выполнения Microsoft VBScript
Error code: 0
Code text:
Error description: Несоответствие типа: 'stamp.ksGetStampColumnText'

Re: COM передать параметр по ссылке

давайте еще такой вариант (правда он имеет ряд плохих эффектов)

Set IStamp = stamp.ksGetStampColumnText( CLng(numb) )  

(изменено: Bolshakov_AV, 15 декабря 2010 14:57:31)

Re: COM передать параметр по ссылке

Та же ошибка.

Попробовал на html - на VBScript и JScript ошибка та же

<HTML>
<HEAD>

<SCRIPT LANGUAGE="VBScript">
Sub test
   

    kStarted = 0      
    
    fileName = "D:\Bolshakov_AV\файло компасовое\Сопло\16.2-9.cdw"
    
    On Error Resume Next                                                              
    Set iKompasObject = GetObject(,"KOMPAS.Application.5")  'это если уже запущен
    If Err Then                                                   
        Set iKompasObject = CreateObject("KOMPAS.Application.5") 
        kStarted = 1
        Err.Clear
    End If
    On Error goto 0
    
                    
    iKompasObject.Visible = True ' показать компас 

    'открыть выгруженный файл
    Set Doc = iKompasObject.Document2D 
    res = Doc.ksOpenDocument( fileName, False ) 
    
    Set DocParams = iKompasObject.GetParamStruct(35)
    
    Call Doc.ksGetObjParam(Doc.reference, DocParams, -1) ' ALLPARAM

    If ((res=True) And (Doc.reference<>0) And (Not Doc Is Nothing)) Then
                           
        Set stamp = Doc.GetStamp()
                                  
        If (Not stamp Is Nothing) Then
           
            If (stamp.ksOpenStamp() = 1) Then

        numb=CLng(1) 


                stamp.ksColumnNumber( numb )
                
                'stamp.ksClearStamp(0) 'работает
                 
                'так и не работает
                Dim numb
                
                numb=CLng(1)      

                Set IStamp = stamp.ksGetStampColumnText( numb ) ' та же ошибка!
                
                stamp.ksCloseStamp()
            
            End If
        
        End If
         
        Doc.ksSaveDocument( fileName )
        Doc.ksCloseDocument()                                         
   
    End If
    

End Sub
</SCRIPT>

<SCRIPT LANGUAGE="JScript">
function test2() {
   
    var kStarted = 0;      
    
    var fileName = "D:\\Bolshakov_AV\\файло компасовое\\Сопло\\16.2-9.cdw";

    var iKompasObject = new ActiveXObject("KOMPAS.Application.5");
    iKompasObject.Visible = true;

    var Doc = iKompasObject.Document2D(); 
    res = Doc.ksOpenDocument( fileName, false ); 

    var stamp = Doc.GetStamp();

    res = stamp.ksOpenStamp();


    if ( res == 1) {

    
    var numb=1;

           
      stamp.ksColumnNumber( numb );
               
      //stamp.ksClearStamp(0);

    var IStamp = stamp.ksGetStampColumnText( numb );//ошибка

    stamp.ksCloseStamp();


   }

    alert("end");
  
};            
</SCRIPT>

</SCRIPT>
</HEAD>

<BODY LANGUAGE="VBScript" onLoad="test2">
   [...]
</BODY>
</HTML>

Re: COM передать параметр по ссылке

да язык то один.

Мы в подобных случаях в нашем АПИ (да и все остальные тоже) для скриптовых языков специальную обертку делали. Но какое сообщение об ошибке при этом идет не скажу точно, чтобы подтвердить что это именно тот случай.

Re: COM передать параметр по ссылке

Наверное придется делать библиотеку-прослойку на Delphi.

Re: COM передать параметр по ссылке

Нет ли у кого примера библиотеки на дельфи?

Делаю стандартно:
1. Делаю библиотеку:
File - New - Other - ActiveX - ActiveX Library
2. Делаю File - New - Other - ActiveX - Automation Object

Делаю в нем New Interface, создаю method. Описываю там действия соединения с компасом.
Компас открывает штамп, у штампа открываем columnText.
Теперь мне как-то нужно вернуть IDispacth* на него. А там только тип только HRESULT.
Как вернуть указатель?
Есть там New DispInterface, тогда у него методы могут возвращать IDispacth*.
Но их реализация в тексте вообще не появляется.

Или я может вообще не в том направлении копаю?

Re: COM передать параметр по ссылке

Если хотите пользовать из скриптов - то только  DispInterface можно пользовать, так как только он позволяет делать позднее связывание.

Можно завести PropertyGet,  можно завести метод задав ему один и параметров как [out, retval]

Re: COM передать параметр по ссылке

Олег Зырянов пишет:

Если хотите пользовать из скриптов - то только  DispInterface можно пользовать, так как только он позволяет делать позднее связывание.

Я так и понял. Но как это делать - до меня не дошло. Если писать методы в просто Interface то среда Delphi автоматически создает их реализацию, а вот при DispInterface - нет. В общем чувствую что надо много читать про это дело и понимать.

Олег Зырянов пишет:

Можно завести PropertyGet,  можно завести метод задав ему один и параметров как [out, retval]

Сроки поджимают, поэтому сделал именно так: через методы передаю параметры (типа ОткрытьКомпас, ОткрытьФайл(такой_то), СчитатьДанныеИзШтампа(номер_ячейки)), а через свойства считываю результат (ПолучитьСчитанныеДанные()).

Но хотелось бы разобраться в этом вопросе и переделать более красиво и правильно.

Re: COM передать параметр по ссылке

Добрый день! Я тоже столкнулся с этой проблемой и вот что мне ответили в Асконе..

VB скрипт не умеет работать с параметрами функций, передаваемыми через указатель, поэтому функции типа ksGetStampColumnText, где есть параметр numb, объявленный как указатель на long, в VB скрипт не работают.

И если можно поделитесь исходниками библиотеки, которую Вы делали/

(изменено: Bolshakov_AV, 18 января 2011 15:34:53)

Re: COM передать параметр по ссылке

andrew1233 пишет:

VB скрипт не умеет работать с параметрами функций, передаваемыми через указатель, поэтому функции типа ksGetStampColumnText, где есть параметр numb, объявленный как указатель на long, в VB скрипт не работают.

Мне ответили то же самое. Послали или в "нормальный" бейсик, или в нормальные языки Си/Дельфи или в Питон.

andrew1233 пишет:

И если можно поделитесь исходниками библиотеки, которую Вы делали/

Я так и не разобрался с Dispatch так что написано не красиво.