Package Skype4Py :: Package API :: Module windows
[frames] | no frames]

Source Code for Module Skype4Py.API.windows

  1  ''' 
  2  Low level Skype for Windows interface implemented 
  3  using Windows messaging. Uses direct WinAPI calls 
  4  through ctypes module. 
  5   
  6  This module handles the options that you can pass to L{ISkype.__init__<skype.ISkype.__init__>} 
  7  for Windows machines. 
  8   
  9  No options are currently supported. 
 10  ''' 
 11   
 12  import threading 
 13  import time 
 14  import weakref 
 15  from ctypes import * 
 16  from Skype4Py.API import ICommand, _ISkypeAPIBase 
 17  from Skype4Py.enums import * 
 18  from Skype4Py.errors import ISkypeAPIError 
 19   
 20  try: 
 21      WNDPROC = WINFUNCTYPE(c_long, c_int, c_uint, c_int, c_int) 
 22  except NameError: 
 23      # This will allow importing of this module on non-Windows machines. It won't work 
 24      # of course but this will allow building documentation on any platform. 
 25      WNDPROC = c_void_p 
 26   
27 -class _WNDCLASS(Structure):
28 _fields_ = [('style', c_uint), 29 ('lpfnWndProc', WNDPROC), 30 ('cbClsExtra', c_int), 31 ('cbWndExtra', c_int), 32 ('hInstance', c_int), 33 ('hIcon', c_int), 34 ('hCursor', c_int), 35 ('hbrBackground', c_int), 36 ('lpszMenuName', c_char_p), 37 ('lpszClassName', c_char_p)]
38 39
40 -class _MSG(Structure):
41 _fields_ = [('hwnd', c_int), 42 ('message', c_uint), 43 ('wParam', c_int), 44 ('lParam', c_int), 45 ('time', c_int), 46 ('pointX', c_long), 47 ('pointY', c_long)]
48 49
50 -class _COPYDATASTRUCT(Structure):
51 _fields_ = [('dwData', POINTER(c_uint)), 52 ('cbData', c_uint), 53 ('lpData', c_char_p)]
54 PCOPYDATASTRUCT = POINTER(_COPYDATASTRUCT) 55 56 _WM_QUIT = 0x12 57 _WM_COPYDATA = 0x4A 58 59 _HWND_BROADCAST = 0xFFFF 60 61 62
63 -class _ISkypeAPI(_ISkypeAPIBase):
64 - def __init__(self, handler, opts):
65 _ISkypeAPIBase.__init__(self, opts) 66 if opts: 67 raise TypeError('Unexpected parameter(s): %s' % ', '.join(opts.keys())) 68 self.hwnd = None 69 self.Skype = None 70 self.RegisterHandler(handler) 71 self._SkypeControlAPIDiscover = windll.user32.RegisterWindowMessageA('SkypeControlAPIDiscover') 72 self._SkypeControlAPIAttach = windll.user32.RegisterWindowMessageA('SkypeControlAPIAttach') 73 windll.user32.GetWindowLongA.restype = c_ulong 74 self.start() 75 # wait till the thread initializes 76 while not self.hwnd: 77 time.sleep(0.01)
78
79 - def run(self):
80 self.DebugPrint('thread started') 81 if not self.CreateWindow(): 82 self.hwnd = None 83 return 84 85 Msg = _MSG() 86 pMsg = pointer(Msg) 87 while self.hwnd and windll.user32.GetMessageA(pMsg, self.hwnd, 0, 0): 88 windll.user32.TranslateMessage(pMsg) 89 windll.user32.DispatchMessageA(pMsg) 90 91 self.DestroyWindow() 92 self.hwnd = None 93 self.DebugPrint('thread finished')
94
95 - def Close(self):
96 # if there are no active handlers 97 if self.NumOfHandlers() == 0: 98 if self.hwnd: 99 windll.user32.PostMessageA(self.hwnd, _WM_QUIT, 0, 0) 100 while self.hwnd: 101 time.sleep(0.01) 102 self.Skype = None 103 self.DebugPrint('closed')
104
105 - def SetFriendlyName(self, FriendlyName):
106 self.FriendlyName = FriendlyName 107 if self.Skype: 108 self.SendCommand(ICommand(-1, 'NAME %s' % FriendlyName))
109
110 - def GetForegroundWindow(self):
111 fhwnd = windll.user32.GetForegroundWindow() 112 if fhwnd: 113 # awahlig (7.05.2008): 114 # I've found at least one app (RocketDock) that had window style 8 set. 115 # This is odd since windows header files do not contain such a style. 116 # Doing message exchange while this window is a foreground one, causes 117 # lockups if some operations on client UI are involved (for example 118 # sending a 'FOCUS' command). Therefore, we will set our window as 119 # the foreground one for the transmission time. 120 if windll.user32.GetWindowLongA(fhwnd, -16) & 8 == 0: 121 fhwnd = None 122 return fhwnd
123
124 - def __Attach_ftimeout(self):
125 self.Wait = False
126
127 - def Attach(self, Timeout=30000, Wait=True):
128 if self.Skype: 129 return 130 if not self.isAlive(): 131 raise ISkypeAPIError('Skype API closed') 132 self.DebugPrint('->', 'SkypeControlAPIDiscover') 133 fhwnd = self.GetForegroundWindow() 134 try: 135 if fhwnd: 136 windll.user32.SetForegroundWindow(self.hwnd) 137 if not windll.user32.SendMessageTimeoutA(_HWND_BROADCAST, self._SkypeControlAPIDiscover, 138 self.hwnd, None, 2, 5000, None): 139 raise ISkypeAPIError('Could not broadcast Skype discover message') 140 # wait (with timeout) till the WindProc() attaches 141 self.Wait = True 142 t = threading.Timer(Timeout / 1000, self.__Attach_ftimeout) 143 if Wait: 144 t.start() 145 while self.Wait and self.AttachmentStatus not in (apiAttachSuccess, apiAttachRefused): 146 if self.AttachmentStatus == apiAttachPendingAuthorization: 147 # disable the timeout 148 t.cancel() 149 elif self.AttachmentStatus == apiAttachAvailable: 150 # rebroadcast 151 self.DebugPrint('->', 'SkypeControlAPIDiscover') 152 windll.user32.SetForegroundWindow(self.hwnd) 153 if not windll.user32.SendMessageTimeoutA(_HWND_BROADCAST, self._SkypeControlAPIDiscover, 154 self.hwnd, None, 2, 5000, None): 155 raise ISkypeAPIError('Could not broadcast Skype discover message') 156 time.sleep(0.01) 157 t.cancel() 158 finally: 159 if fhwnd: 160 windll.user32.SetForegroundWindow(fhwnd) 161 # check if we got the Skype window's hwnd 162 if self.Skype: 163 self.SendCommand(ICommand(-1, 'PROTOCOL %s' % self.Protocol)) 164 elif not self.Wait: 165 raise ISkypeAPIError('Skype attach timeout')
166
167 - def IsRunning(self):
168 # TZap is for Skype 4.0, tSk for 3.8 series 169 return bool(windll.user32.FindWindowA('TZapMainForm.UnicodeClass', None) or \ 170 windll.user32.FindWindowA('tSkMainForm.UnicodeClass', None))
171
172 - def get_skype_path(self):
173 key = c_long() 174 # try to find Skype in HKEY_CURRENT_USER registry tree 175 if windll.advapi32.RegOpenKeyA(0x80000001, 'Software\\Skype\\Phone', byref(key)) != 0: 176 # try to find Skype in HKEY_LOCAL_MACHINE registry tree 177 if windll.advapi32.RegOpenKeyA(0x80000002, 'Software\\Skype\\Phone', byref(key)) != 0: 178 raise ISkypeAPIError('Skype not installed') 179 pathlen = c_long(512) 180 path = create_string_buffer(pathlen.value) 181 if windll.advapi32.RegQueryValueExA(key, 'SkypePath', None, None, path, byref(pathlen)) != 0: 182 windll.advapi32.RegCloseKey(key) 183 raise ISkypeAPIError('Cannot find Skype path') 184 windll.advapi32.RegCloseKey(key) 185 return path.value
186
187 - def Start(self, Minimized=False, Nosplash=False):
188 args = [] 189 if Minimized: 190 args.append('/MINIMIZED') 191 if Nosplash: 192 args.append('/NOSPLASH') 193 try: 194 if self.hwnd: 195 fhwnd = self.GetForegroundWindow() 196 if fhwnd: 197 windll.user32.SetForegroundWindow(self.hwnd) 198 if windll.shell32.ShellExecuteA(None, 'open', self.get_skype_path(), ' '.join(args), None, 0) <= 32: 199 raise ISkypeAPIError('Could not start Skype') 200 finally: 201 if self.hwnd and fhwnd: 202 windll.user32.SetForegroundWindow(fhwnd)
203
204 - def Shutdown(self):
205 try: 206 if self.hwnd: 207 fhwnd = self.GetForegroundWindow() 208 if fhwnd: 209 windll.user32.SetForegroundWindow(self.hwnd) 210 if windll.shell32.ShellExecuteA(None, 'open', self.get_skype_path(), '/SHUTDOWN', None, 0) <= 32: 211 raise ISkypeAPIError('Could not shutdown Skype') 212 finally: 213 if self.hwnd and fhwnd: 214 windll.user32.SetForegroundWindow(fhwnd)
215
216 - def CreateWindow(self):
217 # window class has to be saved as property to keep reference to self.WinProc 218 self.window_class = _WNDCLASS(3, WNDPROC(self.WinProc), 0, 0, 219 windll.kernel32.GetModuleHandleA(None), 220 0, 0, 0, None, 'Skype4Py.%d' % id(self)) 221 222 wclass = windll.user32.RegisterClassA(byref(self.window_class)) 223 if wclass == 0: 224 return False 225 226 self.hwnd = windll.user32.CreateWindowExA(0, 'Skype4Py.%d' % id(self), 'Skype4Py', 227 0xCF0000, 0x80000000, 0x80000000, 228 0x80000000, 0x80000000, None, None, 229 self.window_class.hInstance, 0) 230 if self.hwnd == 0: 231 windll.user32.UnregisterClassA('Skype4Py.%d' % id(self), None) 232 return False 233 234 return True
235
236 - def DestroyWindow(self):
237 if not windll.user32.DestroyWindow(self.hwnd): 238 return False 239 240 if not windll.user32.UnregisterClassA('Skype4Py.%d' % id(self), None): 241 return False 242 243 return True
244
245 - def WinProc(self, hwnd, uMsg, wParam, lParam):
246 if uMsg == self._SkypeControlAPIAttach: 247 self.DebugPrint('<-', 'SkypeControlAPIAttach', lParam) 248 if lParam == apiAttachSuccess: 249 self.Skype = wParam 250 elif lParam in (apiAttachRefused, apiAttachNotAvailable, apiAttachAvailable): 251 self.Skype = None 252 self.SetAttachmentStatus(lParam) 253 return 1 254 elif uMsg == _WM_COPYDATA and wParam == self.Skype and lParam: 255 copydata = cast(lParam, PCOPYDATASTRUCT).contents 256 com8 = copydata.lpData[:copydata.cbData - 1] 257 com = com8.decode('utf-8') 258 self.DebugPrint('<-', repr(com)) 259 if com.startswith(u'#'): 260 p = com.find(u' ') 261 Command = self.CommandsStackPop(int(com[1:p])) 262 if Command: 263 Command.Reply = com[p + 1:] 264 if Command.Blocking: 265 Command._event.set() 266 del Command._event 267 else: 268 Command._timer.cancel() 269 del Command._timer 270 self.CallHandler('rece', Command) 271 else: 272 self.CallHandler('rece_api', com[p + 1:]) 273 else: 274 self.CallHandler('rece_api', com) 275 return 1 276 elif uMsg == apiAttachAvailable: 277 self.DebugPrint('<-', 'apiAttachAvailable') 278 self.Skype = None 279 self.SetAttachmentStatus(uMsg) 280 return 1 281 return windll.user32.DefWindowProcA(c_int(hwnd), c_int(uMsg), c_int(wParam), c_int(lParam))
282
283 - def SendCommand(self, Command):
284 for retry in xrange(2): 285 if not self.Skype: 286 self.Attach(Command.Timeout) 287 self.CommandsStackPush(Command) 288 self.CallHandler('send', Command) 289 com = u'#%d %s' % (Command.Id, Command.Command) 290 com8 = com.encode('utf-8') + '\0' 291 copydata = _COPYDATASTRUCT(None, len(com8), com8) 292 if Command.Blocking: 293 Command._event = event = threading.Event() 294 else: 295 Command._timer = timer = threading.Timer(Command.Timeout / 1000.0, self.CommandsStackPop, (Command.Id,)) 296 self.DebugPrint('->', repr(com)) 297 fhwnd = self.GetForegroundWindow() 298 try: 299 if fhwnd: 300 windll.user32.SetForegroundWindow(self.hwnd) 301 if windll.user32.SendMessageA(self.Skype, _WM_COPYDATA, self.hwnd, byref(copydata)): 302 if Command.Blocking: 303 event.wait(Command.Timeout / 1000.0) 304 if not event.isSet(): 305 raise ISkypeAPIError('Skype command timeout') 306 else: 307 timer.start() 308 break 309 else: 310 self.CommandsStackPop(Command.Id) 311 self.Skype = None 312 # let the loop go back and try to reattach but only once 313 finally: 314 if fhwnd: 315 windll.user32.SetForegroundWindow(fhwnd) 316 else: 317 raise ISkypeAPIError('Skype API error, check if Skype wasn\'t closed')
318