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
24
25 WNDPROC = c_void_p
26
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
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
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
76 while not self.hwnd:
77 time.sleep(0.01)
78
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
96
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
109
111 fhwnd = windll.user32.GetForegroundWindow()
112 if fhwnd:
113
114
115
116
117
118
119
120 if windll.user32.GetWindowLongA(fhwnd, -16) & 8 == 0:
121 fhwnd = None
122 return fhwnd
123
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
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
148 t.cancel()
149 elif self.AttachmentStatus == apiAttachAvailable:
150
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
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
168
169 return bool(windll.user32.FindWindowA('TZapMainForm.UnicodeClass', None) or \
170 windll.user32.FindWindowA('tSkMainForm.UnicodeClass', None))
171
173 key = c_long()
174
175 if windll.advapi32.RegOpenKeyA(0x80000001, 'Software\\Skype\\Phone', byref(key)) != 0:
176
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
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
217
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
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
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
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