Question

I am currently trying to write a tool that will make it very easy for a non-computer literate user to backup a SQL Server database.

To do this I am hoping to use an interesting mix of ADO, win32com and adodbapi. Currently I can easily connect to the server and issues a BACKUP DATABASE T-SQL command.

This works, however it often takes a long time for the command to execute (especially on very large databases). To this end I was hoping to capture and parse the InfoMessage event (MSDN) and use it to show a percentage bar/counter.

This I have also managed, I now I'm stuck at the final hurdle, parsing the event. The MSDN docs say that I should be passed either an Error or Errors object in the pError parameter. However win32com passes me a PyIUnknown object which I don't know how to deal with.

Below is the code that I have written so far:

import win32com
import pythoncom
import adodbapi
from win32com.client import gencache
gencache.EnsureModule('{2A75196C-D9EB-4129-B803-931327F72D5C}', 0, 2, 8)

defaultNamedOptArg=pythoncom.Empty
defaultNamedNotOptArg=pythoncom.Empty
defaultUnnamedArg=pythoncom.Empty

class events():
    def OnInfoMessage(self, pError, adStatus, pConnection):
        print 'A', pError
        #print 'B', adStatus
        #print 'C', pConnection

# This is taken from the makepy file
#    def OnCommitTransComplete(self, pError=defaultNamedNotOptArg, adStatus=defaultNamedNotOptArg, pConnection=defaultNamedNotOptArg): pass
    def OnWillExecute(self, Source=defaultNamedNotOptArg, CursorType=defaultNamedNotOptArg, LockType=defaultNamedNotOptArg, Options=defaultNamedNotOptArg
            , adStatus=defaultNamedNotOptArg, pCommand=defaultNamedNotOptArg, pRecordset=defaultNamedNotOptArg, pConnection=defaultNamedNotOptArg):
        return Source
#    def OnDisconnect(self, adStatus=defaultNamedNotOptArg, pConnection=defaultNamedNotOptArg): pass
    def OnExecuteComplete(self, RecordsAffected=defaultNamedNotOptArg, pError=defaultNamedNotOptArg, adStatus=defaultNamedNotOptArg, pCommand=defaultNamedNotOptArg
            , pRecordset=defaultNamedNotOptArg, pConnection=defaultNamedNotOptArg): pass
        #print pError
    def OnWillConnect(self, ConnectionString=defaultNamedNotOptArg, UserID=defaultNamedNotOptArg, Password=defaultNamedNotOptArg, Options=defaultNamedNotOptArg
            , adStatus=defaultNamedNotOptArg, pConnection=defaultNamedNotOptArg): pass
#    def OnConnectComplete(self, pError=defaultNamedNotOptArg, adStatus=defaultNamedNotOptArg, pConnection=defaultNamedNotOptArg): pass
#    def OnBeginTransComplete(self, TransactionLevel=defaultNamedNotOptArg, pError=defaultNamedNotOptArg, adStatus=defaultNamedNotOptArg, pConnection=defaultNamedNotOptArg):pass
#    def OnRollbackTransComplete(self, pError=defaultNamedNotOptArg, adStatus=defaultNamedNotOptArg, pConnection=defaultNamedNotOptArg): pass




if __name__ == '__main__':

    pythoncom.CoInitialize()
    conn = win32com.client.DispatchWithEvents("ADODB.Connection", events)
    print dir(conn)
    conn.ConnectionString = 'Initial Catalog=test; Data Source=HPDX2250RAAZ\\SQLEXPRESS; Provider=SQLOLEDB.1; Integrated Security=SSPI'
    conn.CommandTimeout = 30
    print conn.ConnectionString
    conn.Open()

    con = adodbapi.Connection(conn)

    c = con.cursor()
    import time
    print 'Execute'
    time.sleep(1)
    c.execute(u"BACKUP DATABASE [test] TO DISK = N'c:/test/test2' WITH STATS = 1")
    print 'Done Execute'

Can anyone extract the Informational messages from the events?

This is it implemented in VB (I think)

For an example of one of these messages start up SQL Server Management Studio and run a backup using a script (you can generate the script using the backup dialogue and the script button in the top left). You will notice that when you run the script the messages box will populate with percentage complete messages. These are what I want.

Edit:

Below is the new code that I'm using to interrogate the COM objects that get passed to the InfoMessage. This is based on the answer below, I'm putting it here in case anyone else needs it.

def OnInfoMessage(self, pError, adStatus, pConnection):
    print 'Info Message'
    a = pError.QueryInterface(pythoncom.IID_IDispatch)
    a = win32com.client.Dispatch(a)
    print a.Description
    print a.Number
    print a.Source
    #print 'B', adStatus
    c = pConnection.QueryInterface(pythoncom.IID_IDispatch)
    c = win32com.client.Dispatch(c)
    print c.Errors.Count
    print c.Errors.Item(0).Description
    print c.Errors.Clear()
    print 'c', adStatus
Was it helpful?

Solution

Reading MSDN, it seems only Error objects should get passed to the event handlers. If there are multiple errors, you can get them from the Errors collection of your Connection object. So you should only expect Error objects to get passed to InfoMessage(). If you get PyIUnknown instead, maybe you can try to call QueryInterface() on it and request IDispatch? You can also try to request the specific custom interface Error uses, but I don't remember if Pythoncom supports custom (i.e. non-IDispatch) interfaces, and my internet is crawling right now so I can't check so you'll have to check that yourself. Anyway, IDispatch should work no matter what, since that's what VB6 uses.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top