COM Clarion. _Package2.LoadfromSQLServer(...)

ODBC

Модератор: Andrew™

Правила форума
При написании вопроса или обсуждении проблемы, не забывайте указывать версию Clarion который Вы используете.
А так же пользуйтесь спец. тегами при вставке исходников!!!
Ответить
Гость

Сообщение Гость »

Привет всем!

Есть ли у кого опыт успешной работы с DTS из Clarion? DtsRun.exe - не интересно, нужно честно повторить функционал с использованием COM, который на VB занимает всего несколько строк:

Код: Выделить всё

Dim objPackage As Package2

Private Sub ExecMyDTS_Click()
    objPackage.GlobalVariables.Item("FileToProcess").Value = "c:\simple.txt"
    objPackage.Execute
End Sub

Private Sub Form_Load()
Set objPackage = New DTS.Package2
    objPackage.LoadFromSQLServer "localhost", "", "", "256", , , , "HelloFromDTS" '<--- *Застрял на реализации в Clarion этого оператора
End Sub

Private Sub Form_UnLoad(Cancel As Integer)
    objPackage.UnInitialize
    Set objPackage = Nothing
End Sub
Пользуюсь Clarion v.6.1 (Build 9026) + PWCOM классы (которые превратились в \Libsrc\svcom.* в поставке Clarion6)

Успешно получаю указатель на интерфейс _Package2 (dtspkg.dll), тривиальные методы интерфейса (например, _Package2
.GetDTSVersionInfo(pVersionMajor, pVersionMinor, pVersionBuild, pVersionComments)) успешно отрабатывают, а при попытке выполнить:
hr = dtsPackage2.LoadFromSQLServer('localhost','','',256,,,,'HelloFromDTS',)
получаю hr <> S_OK, интерпретируемый как "Need to run the object to perform this operation".

OleRun(address(IUnknown)) выполняется внутри базового pwCOMObject класса ...

Из VB, DtsRun.exe, Enterprise Manager DTS package отрабатывает без проблем. Собственно сам DTS package состоит из единственной AciveX Script Task следующего тривиального содержания:

Код: Выделить всё

'**********************************************************************
'  Visual Basic ActiveX Script
'**********************************************************************

Function Main()
        Main = DTSTaskExecResult_Success
        MsgBox "Hello From DTS! " , 0 , "HelloFromDTS"          
End Function
Переписка с Clarion + COM guru - Jim Kane не помогла, увы, ...

Если кто-нибудь одолел дорогу, упомянутую в теме, поделитесь, пожалуйста.

P.S. Не выживет Clarion с такими дырами в поддержке COM, а жаль, ребеночек-то был симпатичный. MS уже расстается с COM (на словах, по-крайней мере), а в Clarion с COM работать приходится через ..., ну, да вы и так знаете, через что.

__________
www.newmail.ru -- Новая Почта: все по-новому.

(Добавление)

Раз некоторые методы вызываются, то могу посоветовать только заменить вызов hr = dtsPackage2.LoadFromSQLServer('localhost','','',256,,,,'HelloFromDTS',) на прямой вызов метода интерфейса с ручным преобразованием STRING в BSTR.
Кстати, в svcom.clw (и соответственно в pw) была грубейшая ошибка в классе CStr, хотя сейчас она исправлена.

С уважением, Михаил
Кстати, в svcom.clw (и соответственно в pw) была грубейшая ошибка в классе CStr, хотя сейчас она исправлена.
Напиши про эту ошибку, плиз. Где, в чём, как исправлена. Очень надо, однако.
Или ссылку дай на публичный ресурс.

Александр Агеев (aageev@satren.ru)
Написал: ClaList(2)
Гость

Сообщение Гость »

Виноват, не в CStr, а в CWideStr.

Что было:

Код: Выделить всё

CWideStr.Init procedure(*string str, byte bSelfCleaning)

sz          &cstring
dwBytes     long

  code
    sz &= new cstring(len(clip(str)))
?   assert(~sz &= null)
    if ~sz &= null
      dwBytes = self.Init(sz, bSelfCleaning)
      dispose(sz)
    end
    return dwBytes
что стало:

Код: Выделить всё

CWideStr.Init procedure(*string str, byte bSelfCleaning)

sz          &cstring
dwBytes     long

  code
    sz &= new cstring(len(clip(str)) + 1)
?   assert(~sz &= null)
    if ~sz &= null
      sz = clip(str)
      dwBytes = self.Init(sz, bSelfCleaning)
      dispose(sz)
    end
    return dwBytes
то есть не было инициализации sz, соответственно получали мусор, да и размер
sz задавался неверно. То же и в версии pwcom, что есть у меня (от 2002г ;^)
PWWideStr.Init procedure(*string str, short fSelfCleaning)

С уважением, Михаил

(Добавление)

Спасибо.

То же есть и в Bstring в 5.5. Не знаю, исправили они в 6-ке или нет...

Александр Агеев
Написал: ClaList(2)
Гость

Сообщение Гость »

Hello Дуга Михаил,

Спасибо за Ваш ответ, биться одному с COM - довольно утомительно.

Сейчас у меня PWCOM v.1.4, указанная дата последней правки 10/25/2003. Перешел с svCOM, т.к. кажется, что отец-основатель COM классов развивает именно эту версию, а не svCOM. Taк, например, в svCOM вырезана (?) реализация процедуры интерпретации кода завершения hr, нет поддержки формирования "пустого" variant типа.

pwWideStr, PWBStr, порождены от pwSTR. Судя по активному использованию pwCOM в xmlFUSE ошибка в инициализации pwWideStr устранена, посмотрю, однако.

Мне кажется, что дело в предаваемых параметрах LoadFromSQLServer. Ее объявление у меня выглядит так (пользуюсь для генерации прототипов RTLib.exe от Jim Kane):

Код: Выделить всё

LoadFromSQLServer  Procedure(Bstring ServerName, !
                             <Bstring ServerUserName>,!         
                             <Bstring ServerPassword>,!
                             DTSSQLServerStorageFlags Flags=DTSSQLStgFlag_UseTrustedConnection,!
                             <Bstring PackagePassword>,!
                             <Bstring PackageGuid>,!
                             <Bstring PackageVersionGuid>,!
                             <Bstring PackageName>,!
                             <*tVariant pVarPersistStgOfHost>),hresult,raw,proc    
!Function[IN],[IN][OPT],[IN][OPT],[IN][OPT][HasDefault],[IN][OPT],[IN][OPT],[IN][OPT],[IN][OPT],[IN][OPT][HasDefault]
Пробуя всевозможные комбинации по передаче параметров я вижу, что диагностика об ошибке меняется при вызове LoadFromSQLServer. Что нужно передавать вместо опускаемых параметров типа BString (aka long)? Например, для lpBstrServerUserName и lpBstrServerPassword. Я пробовал:
- инициализировать bstrServerUserName пустой cstring '';
- просто опускать параметр bstrServerUserName;
- задавать lpBstrServerUserName без вызова bstrServerName.init(szParam);
- передавать 0.
Для передачи "пропущенного" Variant типа данных я использую pVtMissing &= _vt(VtMissing), чтобы COM согласился, что эти данные пропущены. А что передавать вместо "пропущенных" BString (aka long)?

При этом код ошибки в hr меняется, но куда дальше рулить - ??? Метод тупого перебора того, что передать в параметрах я исчерпал.

Например, если вместо DTSSQLServerStorageFlags Flags=DTSSQLStgFlag_UseTrustedConnection передать 0 (т.е. запросить авторизацию на SQL Server), то получаю диагностику "COM: unspecified error".

Подготовка параметров и вызов LoadFromSQLServer выглядит у меня так:

Код: Выделить всё

bstrServerName              pwBStr
bstrServerUserName          pwBStr
bstrServerPassword          pwBStr
bstrPackagePassword         pwBStr
bstrPackageGuid             pwBStr
bstrPackageVersionGuid      pwBStr
bstrPackageName             pwBStr
lpBstrServerName            long
lpBstrServerUserName        long
lpBstrServerPassword        long
lpBstrPackagePassword       long
lpBstrPackageGuid           long
lpBstrPackageVersionGuid    long
lpBstrPackageName           long
szParam                     cstring(256)
pVtMissing                  &tVariant

     code

!Convert LoadFromSQLServer parameters to bstring
    szParam = clip(par_ServerName)                      !Parameter #1: ServerName
    bstrServerName.init(szParam)
    lpBstrServerName = bstrServerName.GetStr()
       
    szParam = clip(par_PackageName)                     !Parameter #8: PackageName
    bstrPackageName.init(szParam)
    lpBstrPackageName = bstrPackageName.GetStr()

    pVtMissing &= _vt(VtMissing)                        !Parameter #9: pVarPersistStgOfHost = Empty Variant structure

    hr = self.I_Package2.LoadFromSQLServer(lpBstrServerName,                    !
                                           lpBstrServerUserName,                !
                                           lpBstrServerPassword,                !
                                           DTSSQLStgFlag_UseTrustedConnection,  ! !Use Windows Authentication to connect to an
instance of SQL Server
                                           lpBstrPackagePassword,               !
                                           lpBstrPackageGuid,                   !
                                           lpBstrPackageVersionGuid,            !
                                           lpBstrPackageName,                   !
                                           pVtMissing)

                                          ...

!Free memory used by bstrings
    bstrServerName.release()
    bstrServerUserName.release()
    bstrServerPassword.release()
    bstrPackagePassword.release()
    bstrPackageGuid.release()
    bstrPackageVersionGuid.release()
    bstrPackageName.release()
С уважением,
Сергей Смирнов.
Написал: ClaList(2)
Гость

Сообщение Гость »

Перешел с svCOM, т.к. кажется, что отец-основатель COM классов развивает именно эту версию, а не svCOM. Taк, например, в svCOM вырезана (?) реализация процедуры интерпретации кода завершения hr, нет поддержки формирования "пустого" variant типа.
Для передачи "пропущенного" Variant типа данных я использую pVtMissing &= _vt(VtMissing), чтобы COM согласился, что эти данные пропущены. А что передавать вместо "пропущенных" BString (aka long)?
Я бы избегал "опущенных" аргументов, и все методы в описании интерфейса переписал бы без угловых скобок <Bstring ServerUserName>, так как был негативный опыт... Пропущенные BSTR передал бы как пустые строки, а VARIANT создал бы из пустой строки.
Начать можно с заведомо неверного вызова (например, все параметры пустые).
DtsRun на такой вызов ругается 80004005, можно попробовать добиться того же результата и постепенно корректировать вызов до полной победы

Михаил
Написал: ClaList(2)
Гость

Сообщение Гость »

Уфх, заработало!

Вся проблема, как и предполагал, состояла в правильном заполнении передаваемых параметров, которые помечены как [opt]:

Ниже приведен прототип LoadFromSQLServer:

Код: Выделить всё

LoadFromSQLServer Procedure (Bstring ServerName,Bstring ServerUserName,Bstring ServerPassword,DTSSQLServerStorageFlags Flags,Bstring
PackagePassword,Bstring PackageGuid,Bstring PackageVersionGuid,Bstring PackageName,*tVariant pVarPersistStgOfHost),hresult,raw,proc

OleView описывает прототип следующим образом:

Код: Выделить всё

!Function[IN],[IN][OPT],[IN][OPT],[IN][OPT][HasDefault],[IN][OPT],[IN][OPT],[IN][OPT],[IN][OPT],[IN][OPT][HasDefault]
Вся загвоздка состояла в том, что нужно было правильно инициализировать [OPT] параметры, чтобы COM опознал их как "пропущенные":

Код: Выделить всё

 ...
bstrServerName              pwBStr
bstrServerUserName          pwBStr
bstrServerPassword          pwBStr
bstrPackagePassword         pwBStr
bstrPackageGuid             pwBStr
bstrPackageVersionGuid      pwBStr
bstrPackageName             pwBStr
lpBstrServerName            long
lpBstrServerUserName        long
lpBstrServerPassword        long
lpBstrPackagePassword       long
lpBstrPackageGuid           long
lpBstrPackageVersionGuid    long
lpBstrPackageName           long
szParam                     cstring(256)
pVtMissing                  &tVariant

        code
 ...
!- for ommitable BString (e.g. lpBstrServerUserName):    
    szParam = ''
    bstrServerUserName.init(szParam)
    lpBstrServerUserName = bstrServerUserName.GetStr()
 ...   
!- for ommitable *tVariant pVarPersistStgOfHost:
    pVtMissing &= _vt(VtMissing)                        



    hr = self.I_Package2.LoadFromSQLServer(lpBstrServerName,                    !
                                           lpBstrServerUserName,                !
                                           lpBstrServerPassword,                !
                                           par_Flags,                           !
                                           lpBstrPackagePassword,               !
                                           lpBstrPackageGuid,                   !
                                           lpBstrPackageVersionGuid,            !
                                           lpBstrPackageName,                   !
                                           pVtMissing)
!--------------------------------------------------------------------------------
Без указанной инициализации "пустых" [OPT] параметров LoadFromSQLServer умирал с неинтерпретируемым кодом завершения (hr)!

Следующая преграда - использование COM collections из Clarion. Есть у кого-нибудь опыт работы с коллекциями?

С уважением,
Сергей Смирнов

Привет, Сергей.
Следующая преграда - использование COM collections из Clarion. Есть у кого-нибудь опыт работы с коллекциями?
Глянь класс RegExp (есть на http://www.clarionlife.net/ и в EasyCom2Inc у ingasoftplus), там реализована IMatchCollection

С уважением, Михаил

(Добавление)

Hello Дуга Михаил,

Спасибо, Михаил, что не оставили в одиночестве в борьбе с Clarion за доступ к COM.
Я бы избегал "опущенных" аргументов, и все методы в описании интерфейса переписал бы без угловых скобок <Bstring ServerUserName>, так как был негативный опыт...
- Действительно, нужно было честно заполнить все [OPT] параметры, именно в этом и была проблема.
Мой опыт оказался таким же как и Ваш.
Параметры опускать никак нельзя, нельзя, также, передавать 0 вместо пропущенных параметров BSTring или Variant.
Пропущенные BSTR передал бы как пустые строки, а VARIANT создал бы из пустой строки.
- Я сделал так: явно заполнил пропускаемые [OPT] параметры BSTring из пустых cstring, для Variant параметра поступил почти также (в PWCOM есть для этого VtMissing), заполнение Variant "пустышкой" выполняется так:

Код: Выделить всё

            ...

bstrServerUserName          pwBStr
lpBstrServerName            long
szParam                     cstring(256)
pVtMissing                  &tVariant
            ...
            code
            ...

    if omitted(3)                                       !Ommitable Parameter #2: ServerUserName
       szParam = ''
    else
       szParam = clip(par_ServerUserName)                  
    .
    bstrServerUserName.init(szParam)
    lpBstrServerUserName = bstrServerUserName.GetStr()


     pVtMissing &= _vt(VtMissing)                        !Parameter #9: pVarPersistStgOfHost 
после этого _Package2.LoadFromSQLServer задышал. Жаль, что я не получил эту подсказку раньше. Впрочем то, что дело в параметрах я понял. Но пока шел по дороге от "понял" до "решил" для данной проблемы я потратил непристойно много времени, пока смог из Clarion объяснить COM, что передаю "пропущенные" параметры.

Код: Выделить всё

    hr = self.I_Package2.LoadFromSQLServer(lpBstrServerName,                    !  
                                           lpBstrServerPassword,                !
                                           par_Flags,                           !
                                           lpBstrPackagePassword,               !
                                           lpBstrPackageGuid,                   !
                                           lpBstrPackageVersionGuid,            !
                                           lpBstrPackageName,                   !
                                           pVtMissing)
После этого LoadFromSQLServer задышал.
С коллекциями тоже справился.

Остались вопросы о некоторых мной обнаруженных, но непонятых хитростях (я описал их в SofVelocity.public.clarion6 Discussion group).
Привожу их здесь, прошу меня простить, на обратный перевод сил нету.

Если есть ответы, подскажите, пожалуйста.

Ну, а следующая проблема, которую мне потребуется решить - перехват в Clarion приложении сообщений об ошибках из выполняемых dts package. Надеюсь, что и это возможно.

С уважением,
Сергей Смирнов.

--------------------------------------------------------------------------------------

Hi Clarioners,

I hope that COM gurus in the Clarion world, could help me to understand 2
following questions:

Q1. About the interface reference counter.

I know about the rule, that when I get the reference to an interface (e.g.
using QueryInterface) then later, I have to use the .Release method to
decrement an internall reference counter of that interface. Working with COM
collections I use the following code to get from _Package2 coclass object the
reference to the IGlobalVariables interface. So, in the CPackage2.destruct
procedure I try to call self.IGlobalVariables.release(). If I do this, then I
get GPF finishing my application. If I don't, then aplication finishes
correctly. I know also, that pwCOMObject releases the IUnknown in its destruct
method...

Код: Выделить всё

!------------------------------------------------
CPackage2   class(PWCOMObject), module('dtspkg.clw'),link('dtspkg.clw', 
_COMLinkMode_),dll(_COMDllMode_)
I_Package2          &_Package2Type, protected           !pointer to the 
_Package2 interface. It will be initialized by CoCreateInstance in init method 
of this class.
IGlobalVariables    &GlobalVariablesType, protected     !pointer to the 
IGlobalVariables interface
IsInitialazed       byte,private                        !True - CPackage2 is initialized

construct           procedure()
destruct            procedure(), virtual    
init                procedure(), proc, HRESULT, virtual !Here is the internal 
CoCreateInstance call
GetDTSVersionInfo   procedure(*LONG VersionMajor,*LONG VersionMinor,*LONG 
VersionBuild,*Bstring VersionComments),proc,HRESULT
LoadFromSQLServer   procedure(string                    
par_ServerName,                                 !
                              string                    
par_ServerUserName,                             !
                              string                    
par_ServerPassword,                             !
                              DTSSQLServerStorageFlags  
par_Flags=DTSSQLStgFlag_UseTrustedConnection,   !
                              string                    
par_PackagePassword,                            !
                              string                    
par_PackageGuid,                                !
                              string                    
par_PackageVersionGuid,                         !
                              string                    
par_PackageName,                                !
                              <*tVariant                par_pVarPersistStgOfHost>),proc, HRESULT
ExecuteIt           procedure(),proc, HRESULT, virtual
PutGlobalVariableValue   procedure(string par_VarName, string 
par_VarValue),proc, HRESULT, virtual
UnInitialize        procedure(),proc, HRESULT          !The Uninitialize method 
clears all state information and releases all related objects, allowing the 
Package object to be reused
            .

                ...
!------------------------------------------------               

                ...
!Getting IGlobalVariables interface             

hr = self.I_Package2.GetGlobalVariables(pgvs)  !GetGlobalVariables 
Procedure(*LONG lpGlobalVariables),hresult,raw,proc !PropGet[OUT][RETVAL]
if hr = S_OK
   self.IGlobalVariables &= (pgvs)             !<-- it is a trick (type customizing)
.
Is it right, that the I_Package2.GetGlobalVariables method increments
internally the reference counter of IGlobalVariables interface like
QueryInterface? If so, then I should explicitly call somewhere later the
IGlobalVariables.Release. I do that using following code in CPackage2.destruct
procedure:

Код: Выделить всё

!------------------------------------------------
CPackage2.destruct  procedure()
nCnt    long

    code
!
!    nCnt = self.IGlobalVariables.release()         !<-- Q1. If it is uncommented 
then I get GPF right here at the end of the application
!    if nCnt = 0
!       self.IGlobalVariables &= null
!    .
!                                                        
!-----------------------------------------------
Q2. About repeatable calling of the I_Package2.LoadFromSQLServer method.
It is a specific question, not a common one, I know.

there is the calling statement in my code:

Код: Выделить всё

    hr = self.I_Package2.LoadFromSQLServer(lpBstrServerName,                   !   
                                           
lpBstrServerUserName,                                        !
                                           
lpBstrServerPassword,                                         !
                                           
par_Flags,                                                            !
                                           
lpBstrPackagePassword,                                     !
                                           
lpBstrPackageGuid,                                             !
                                           
lpBstrPackageVersionGuid,                                 !
                                           
lpBstrPackageName,                                           !
                                           pVtMissing)

    hr = self.IGlobalVariables.GetCount(count)  !<-- A TRICK. I do this only to 
avoid bad hr (COM: Error Unknown, hr = -2147220500 (0x800403EC)) on repeated 
calling the previous hr = self.I_Package2.LoadFromSQLServer statement. ??? 
                                                                                
!What does that mean?  Why do I need call hr = 
self.IGlobalVariables.GetCount(count) ???
    
!-----------------------------------------------    
I found this solution accidentally and I do not understand this trick :-((

Regards,
Sergey Smirnoff.

--------------------------------------
P.S. I use Clarion v.6.1 (9026) + PWCOM classes by PlugwareSolutions.com Ltd
delivered with XMLFuse by ThinkData.com + RTLib.exe by Jim Kane
Написал: ClaList(2)
Ответить