mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 02:34:06 -04:00
Basic DDE wrapper
This commit is contained in:
parent
1fcf1e01b2
commit
01e9cd29ce
143
src/calibre/utils/winreg/dde.py
Normal file
143
src/calibre/utils/winreg/dde.py
Normal file
@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python2
|
||||
# vim:fileencoding=utf-8
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from ctypes import POINTER, WINFUNCTYPE, c_void_p, c_ulong, c_char_p, windll, byref
|
||||
from ctypes.wintypes import BOOL, DWORD, LPCWSTR, UINT
|
||||
|
||||
HCONV = c_void_p # = DECLARE_HANDLE(HCONV)
|
||||
HDDEDATA = c_void_p # = DECLARE_HANDLE(HDDEDATA)
|
||||
HSZ = c_void_p # = DECLARE_HANDLE(HSZ)
|
||||
LPBYTE = c_char_p # POINTER(BYTE)
|
||||
LPDWORD = POINTER(DWORD)
|
||||
LPSTR = c_char_p
|
||||
ULONG_PTR = c_ulong
|
||||
|
||||
DML_ERRORS = {
|
||||
'ADVACKTIMEOUT': (0x4000, 'A request for a synchronous advise transaction has timed out.'),
|
||||
|
||||
'BUSY': (0x4001, 'The response to the transaction caused the DDE_FBUSY flag to be set.'),
|
||||
|
||||
'DATAACKTIMEOUT': (0x4002, 'A request for a synchronous data transaction has timed out.'),
|
||||
|
||||
'DLL_NOT_INITIALIZED': (0x4003, 'A DDEML function was called without first calling the DdeInitialize function, or an invalid instance identifier was passed to a DDEML function.'), # noqa
|
||||
|
||||
'DLL_USAGE': (0x4004, 'An application initialized as APPCLASS_MONITOR has attempted to perform a DDE transaction, or an application initialized as APPCMD_CLIENTONLY has attempted to perform server transactions.'), # noqa
|
||||
|
||||
'EXECACKTIMEOUT': (0x4005, 'A request for a synchronous execute transaction has timed out.'),
|
||||
|
||||
'INVALIDPARAMETER': (0x4006, 'An invalid transaction identifier was passed to a DDEML function. Once the application has returned from an XTYP_XACT_COMPLETE callback, the transaction identifier for that callback function is no longer valid. A parameter failed to be validated by the DDEML. Some of the possible causes follow: The application used a data handle initialized with a different item name handle than was required by the transaction. The application used a data handle that was initialized with a different clipboard data format than was required by the transaction. The application used a client-side conversation handle with a server-side function or vice versa. The application used a freed data handle or string handle. More than one instance of the application used the same object.'), # noqa
|
||||
|
||||
'LOW_MEMORY': (0x4007, 'A DDEML application has created a prolonged race condition (in which the server application outruns the client), causing large amounts of memory to be consumed.'), # noqa
|
||||
|
||||
'MEMORY_ERROR': (0x4008, 'A memory allocation has failed.'),
|
||||
|
||||
'NO_CONV_ESTABLISHED': (0x400a, 'A client\'s attempt to establish a conversation has failed.'),
|
||||
|
||||
'NOTPROCESSED': (0x4009, 'A transaction has failed.'),
|
||||
|
||||
'POKEACKTIMEOUT': (0x400b, 'A request for a synchronous poke transaction has timed out.'),
|
||||
|
||||
'POSTMSG_FAILED': (0x400c, 'An internal call to the PostMessage function has failed.'),
|
||||
|
||||
'REENTRANCY': (0x400d, 'An application instance with a synchronous transaction already in progress attempted to initiate another synchronous transaction, or the DdeEnableCallback function was called from within a DDEML callback function.'), # noqa
|
||||
|
||||
'SERVER_DIED': (0x400e, 'A server-side transaction was attempted on a conversation terminated by the client, or the server terminated before completing a transaction.'), # noqa
|
||||
|
||||
'SYS_ERROR': (0x400f, 'An internal error has occurred in the DDEML.'),
|
||||
|
||||
'UNADVACKTIMEOUT': (0x4010, 'A request to end an advise transaction has timed out.'),
|
||||
|
||||
'UNFOUND_QUEUE_ID': (0x4011, 'An invalid transaction identifier was passed to a DDEML function. Once the application has returned from an XTYP_XACT_COMPLETE callback, the transaction identifier for that callback function is no longer valid.'), # noqa
|
||||
}
|
||||
DML_ERROR_TEXT = {code:text for (code, text) in DML_ERRORS.itervalues()}
|
||||
|
||||
user32 = windll.user32
|
||||
|
||||
DDECALLBACK = WINFUNCTYPE(HDDEDATA, UINT, UINT, HCONV, HSZ, HSZ, HDDEDATA, ULONG_PTR, ULONG_PTR)
|
||||
APPCMD_CLIENTONLY = 0x10
|
||||
CP_WINUNICODE = 1200
|
||||
# See windows/ddeml.h for declaration of struct CONVCONTEXT
|
||||
PCONVCONTEXT = c_void_p
|
||||
XCLASS_FLAGS = 0x4000
|
||||
XTYP_EXECUTE = (0x0050 | XCLASS_FLAGS)
|
||||
|
||||
class DDEError(ValueError):
|
||||
pass
|
||||
|
||||
def init_errcheck(result, func, args):
|
||||
if result != 0:
|
||||
raise DDEError('Failed to initialize DDE client with return code: %x' % result)
|
||||
return args
|
||||
|
||||
def no_errcheck(result, func, args):
|
||||
return args
|
||||
|
||||
def dde_error(instance):
|
||||
errcode = GetLastError(instance)
|
||||
raise DDEError(DML_ERRORS.get(errcode, 'Unknown DDE error code: %x' % errcode))
|
||||
|
||||
def default_errcheck(result, func, args):
|
||||
if (isinstance(result, (int, long)) and result == 0) or (getattr(result, 'value', False) is None):
|
||||
dde_error(args[0])
|
||||
return args
|
||||
|
||||
null = object()
|
||||
|
||||
class a(object):
|
||||
|
||||
def __init__(self, name, typ, default=null, in_arg=True):
|
||||
self.typ=typ
|
||||
if default is null:
|
||||
self.spec=((1 if in_arg else 2), name)
|
||||
else:
|
||||
self.spec=((1 if in_arg else 2), name, default)
|
||||
|
||||
def cwrap(name, restype, *args, **kw):
|
||||
params=(restype,) + tuple(x.typ for x in args)
|
||||
paramflags=tuple(x.spec for x in args)
|
||||
func=WINFUNCTYPE(*params)((name, kw.get('lib', user32)), paramflags)
|
||||
func.errcheck=kw.get('errcheck', default_errcheck)
|
||||
return func
|
||||
|
||||
GetLastError = cwrap('DdeGetLastError', UINT, a('instance', DWORD), errcheck=no_errcheck)
|
||||
|
||||
Initialize = cwrap('DdeInitializeW', UINT, a('instance_p', LPDWORD), a('callback', DDECALLBACK), a('command', DWORD),
|
||||
a('reserved', DWORD, 0), errcheck=init_errcheck)
|
||||
CreateStringHandle = cwrap('DdeCreateStringHandleW', HSZ, a('instance', DWORD), a('string', LPCWSTR), a('codepage', UINT, CP_WINUNICODE))
|
||||
Connect = cwrap('DdeConnect', HCONV, a('instance', DWORD), a('service', HSZ), a('topic', HSZ), a('context', PCONVCONTEXT))
|
||||
FreeStringHandle = cwrap('DdeFreeStringHandle', BOOL, a('instance', DWORD), a('handle', HSZ), errcheck=no_errcheck)
|
||||
ClientTransaction = cwrap('DdeClientTransaction', HDDEDATA, a('data', LPBYTE), a('size', DWORD), a('conversation', HCONV), a('item', HSZ),
|
||||
a('fmt', UINT, 0), a('type', UINT, XTYP_EXECUTE), a('timeout', DWORD, 5000), a('result', LPDWORD, LPDWORD()), errcheck=no_errcheck)
|
||||
FreeDataHandle = cwrap('DdeFreeDataHandle', BOOL, a('data', HDDEDATA), errcheck=no_errcheck)
|
||||
Disconnect = cwrap('DdeDisconnect', BOOL, a('conversation', HCONV), errcheck=no_errcheck)
|
||||
Uninitialize = cwrap('DdeUninitialize', BOOL, a('instance', DWORD), errcheck=no_errcheck)
|
||||
|
||||
def send_dde_command(service, topic, command):
|
||||
instance = DWORD(0)
|
||||
|
||||
def cb(*args):
|
||||
pass # ignore callbacks
|
||||
callback = DDECALLBACK(cb)
|
||||
|
||||
Initialize(byref(instance), callback, APPCMD_CLIENTONLY, 0)
|
||||
hservice = CreateStringHandle(instance, service)
|
||||
htopic = CreateStringHandle(instance, topic)
|
||||
conversation = Connect(instance, hservice, htopic, PCONVCONTEXT())
|
||||
FreeStringHandle(instance, hservice)
|
||||
FreeStringHandle(instance, htopic)
|
||||
data = c_char_p(command)
|
||||
sz = DWORD(len(command) + 1)
|
||||
res = ClientTransaction(data, sz, conversation, HSZ())
|
||||
if res == 0:
|
||||
dde_error(instance)
|
||||
FreeDataHandle(res)
|
||||
Disconnect(conversation)
|
||||
Uninitialize(instance)
|
||||
|
||||
if __name__ == '__main__':
|
||||
send_dde_command('WinWord', 'System', '[REM_DDE_Direct][FileOpen("C:/cygwin64/home/kovid/demo.docx")]')
|
Loading…
x
Reference in New Issue
Block a user