dde 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +60 -0
- data/VERSION +1 -0
- data/bin/dde_main +112 -0
- data/dde.gemspec +94 -0
- data/doc/XLTable_format.doc +0 -0
- data/doc/dde_formats.doc +0 -0
- data/doc/ddeml.d.txt +374 -0
- data/doc/types.txt +159 -0
- data/doc/~$Table_format.doc +0 -0
- data/doc/~$e_formats.doc +0 -0
- data/features/dde.feature +9 -0
- data/features/step_definitions/dde_steps.rb +0 -0
- data/features/support/env.rb +4 -0
- data/lib/dde/app.rb +85 -0
- data/lib/dde/client.rb +53 -0
- data/lib/dde/dde_string.rb +33 -0
- data/lib/dde/monitor.rb +26 -0
- data/lib/dde/server.rb +41 -0
- data/lib/dde/xl_server.rb +32 -0
- data/lib/dde/xl_table.rb +172 -0
- data/lib/dde.rb +8 -0
- data/spec/dde/app_spec.rb +85 -0
- data/spec/dde/client_spec.rb +176 -0
- data/spec/dde/dde_string_spec.rb +40 -0
- data/spec/dde/monitor_spec.rb +81 -0
- data/spec/dde/server_shared.rb +103 -0
- data/spec/dde/server_spec.rb +21 -0
- data/spec/dde/xl_server_spec.rb +51 -0
- data/spec/dde/xl_table_spec.rb +12 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +49 -0
- metadata +127 -0
data/doc/ddeml.d.txt
ADDED
@@ -0,0 +1,374 @@
|
|
1
|
+
/***********************************************************************\
|
2
|
+
* ddeml.d *
|
3
|
+
* *
|
4
|
+
* Windows API header module *
|
5
|
+
* *
|
6
|
+
* Translated from MinGW Windows headers *
|
7
|
+
* by Stewart Gordon *
|
8
|
+
* *
|
9
|
+
* Placed into public domain *
|
10
|
+
\***********************************************************************/
|
11
|
+
module win32.ddeml;
|
12
|
+
pragma(lib, "user32.lib");
|
13
|
+
|
14
|
+
private import win32.basetsd, win32.windef, win32.winnt;
|
15
|
+
|
16
|
+
enum : int {
|
17
|
+
CP_WINANSI = 1004,
|
18
|
+
CP_WINUNICODE = 1200
|
19
|
+
}
|
20
|
+
|
21
|
+
enum : UINT {
|
22
|
+
XTYPF_NOBLOCK = 2,
|
23
|
+
XTYPF_NODATA = 4,
|
24
|
+
XTYPF_ACKREQ = 8
|
25
|
+
}
|
26
|
+
|
27
|
+
enum : UINT {
|
28
|
+
XCLASS_MASK = 0xFC00,
|
29
|
+
XCLASS_BOOL = 0x1000,
|
30
|
+
XCLASS_DATA = 0x2000,
|
31
|
+
XCLASS_FLAGS = 0x4000,
|
32
|
+
XCLASS_NOTIFICATION = 0x8000
|
33
|
+
}
|
34
|
+
|
35
|
+
enum : UINT {
|
36
|
+
XST_NULL,
|
37
|
+
XST_INCOMPLETE,
|
38
|
+
XST_CONNECTED,
|
39
|
+
XST_INIT1,
|
40
|
+
XST_INIT2,
|
41
|
+
XST_REQSENT,
|
42
|
+
XST_DATARCVD,
|
43
|
+
XST_POKESENT,
|
44
|
+
XST_POKEACKRCVD,
|
45
|
+
XST_EXECSENT,
|
46
|
+
XST_EXECACKRCVD,
|
47
|
+
XST_ADVSENT,
|
48
|
+
XST_UNADVSENT,
|
49
|
+
XST_ADVACKRCVD,
|
50
|
+
XST_UNADVACKRCVD,
|
51
|
+
XST_ADVDATASENT,
|
52
|
+
XST_ADVDATAACKRCVD // = 16
|
53
|
+
}
|
54
|
+
|
55
|
+
enum : UINT {
|
56
|
+
XTYP_ERROR = XCLASS_NOTIFICATION | XTYPF_NOBLOCK,
|
57
|
+
XTYP_ADVDATA = 0x0010 | XCLASS_FLAGS,
|
58
|
+
XTYP_ADVREQ = 0x0020 | XCLASS_DATA | XTYPF_NOBLOCK,
|
59
|
+
XTYP_ADVSTART = 0x0030 | XCLASS_BOOL,
|
60
|
+
XTYP_ADVSTOP = 0x0040 | XCLASS_NOTIFICATION,
|
61
|
+
XTYP_EXECUTE = 0x0050 | XCLASS_FLAGS,
|
62
|
+
XTYP_CONNECT = 0x0060 | XCLASS_BOOL | XTYPF_NOBLOCK,
|
63
|
+
XTYP_CONNECT_CONFIRM = 0x0070 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK,
|
64
|
+
XTYP_XACT_COMPLETE = 0x0080 | XCLASS_NOTIFICATION,
|
65
|
+
XTYP_POKE = 0x0090 | XCLASS_FLAGS,
|
66
|
+
XTYP_REGISTER = 0x00A0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK,
|
67
|
+
XTYP_REQUEST = 0x00B0 | XCLASS_DATA,
|
68
|
+
XTYP_DISCONNECT = 0x00C0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK,
|
69
|
+
XTYP_UNREGISTER = 0x00D0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK,
|
70
|
+
XTYP_WILDCONNECT = 0x00E0 | XCLASS_DATA | XTYPF_NOBLOCK,
|
71
|
+
XTYP_MONITOR = 0X00F0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK,
|
72
|
+
XTYP_MASK = 0x00F0,
|
73
|
+
XTYP_SHIFT = 4
|
74
|
+
}
|
75
|
+
|
76
|
+
/+
|
77
|
+
#define TIMEOUT_ASYNC 0xFFFFFFFF
|
78
|
+
#define QID_SYNC 0xFFFFFFFF
|
79
|
+
+/
|
80
|
+
|
81
|
+
enum : UINT {
|
82
|
+
ST_CONNECTED = 1,
|
83
|
+
ST_ADVISE = 2,
|
84
|
+
ST_ISLOCAL = 4,
|
85
|
+
ST_BLOCKED = 8,
|
86
|
+
ST_CLIENT = 16,
|
87
|
+
ST_TERMINATED = 32,
|
88
|
+
ST_INLIST = 64,
|
89
|
+
ST_BLOCKNEXT = 128,
|
90
|
+
ST_ISSELF = 256
|
91
|
+
}
|
92
|
+
|
93
|
+
/+
|
94
|
+
#define CADV_LATEACK 0xFFFF
|
95
|
+
+/
|
96
|
+
|
97
|
+
enum : UINT {
|
98
|
+
DMLERR_NO_ERROR = 0,
|
99
|
+
DMLERR_FIRST = 0x4000,
|
100
|
+
DMLERR_ADVACKTIMEOUT = DMLERR_FIRST,
|
101
|
+
DMLERR_BUSY,
|
102
|
+
DMLERR_DATAACKTIMEOUT,
|
103
|
+
DMLERR_DLL_NOT_INITIALIZED,
|
104
|
+
DMLERR_DLL_USAGE,
|
105
|
+
DMLERR_EXECACKTIMEOUT,
|
106
|
+
DMLERR_INVALIDPARAMETER,
|
107
|
+
DMLERR_LOW_MEMORY,
|
108
|
+
DMLERR_MEMORY_ERROR,
|
109
|
+
DMLERR_NOTPROCESSED,
|
110
|
+
DMLERR_NO_CONV_ESTABLISHED,
|
111
|
+
DMLERR_POKEACKTIMEOUT,
|
112
|
+
DMLERR_POSTMSG_FAILED,
|
113
|
+
DMLERR_REENTRANCY,
|
114
|
+
DMLERR_SERVER_DIED,
|
115
|
+
DMLERR_SYS_ERROR,
|
116
|
+
DMLERR_UNADVACKTIMEOUT,
|
117
|
+
DMLERR_UNFOUND_QUEUE_ID, // = 0x4011
|
118
|
+
DMLERR_LAST = DMLERR_UNFOUND_QUEUE_ID
|
119
|
+
}
|
120
|
+
|
121
|
+
/+
|
122
|
+
#define DDE_FACK 0x8000
|
123
|
+
#define DDE_FBUSY 0x4000
|
124
|
+
#define DDE_FDEFERUPD 0x4000
|
125
|
+
#define DDE_FACKREQ 0x8000
|
126
|
+
#define DDE_FRELEASE 0x2000
|
127
|
+
#define DDE_FREQUESTED 0x1000
|
128
|
+
#define DDE_FAPPSTATUS 0x00ff
|
129
|
+
#define DDE_FNOTPROCESSED 0
|
130
|
+
#define DDE_FACKRESERVED (~(DDE_FACK|DDE_FBUSY|DDE_FAPPSTATUS))
|
131
|
+
#define DDE_FADVRESERVED (~(DDE_FACKREQ|DDE_FDEFERUPD))
|
132
|
+
#define DDE_FDATRESERVED (~(DDE_FACKREQ|DDE_FRELEASE|DDE_FREQUESTED))
|
133
|
+
#define DDE_FPOKRESERVED (~DDE_FRELEASE)
|
134
|
+
#define MSGF_DDEMGR 0x8001
|
135
|
+
#define CBR_BLOCK ((HDDEDATA)0xffffffff)
|
136
|
+
+/
|
137
|
+
|
138
|
+
const DWORD
|
139
|
+
APPCLASS_STANDARD = 0,
|
140
|
+
APPCLASS_MONITOR = 0x00000001,
|
141
|
+
APPCLASS_MASK = 0x0000000F,
|
142
|
+
APPCMD_CLIENTONLY = 0x00000010,
|
143
|
+
APPCMD_FILTERINITS = 0x00000020,
|
144
|
+
APPCMD_MASK = 0x00000FF0,
|
145
|
+
CBF_FAIL_SELFCONNECTIONS = 0x00001000,
|
146
|
+
CBF_FAIL_CONNECTIONS = 0x00002000,
|
147
|
+
CBF_FAIL_ADVISES = 0x00004000,
|
148
|
+
CBF_FAIL_EXECUTES = 0x00008000,
|
149
|
+
CBF_FAIL_POKES = 0x00010000,
|
150
|
+
CBF_FAIL_REQUESTS = 0x00020000,
|
151
|
+
CBF_FAIL_ALLSVRXACTIONS = 0x0003f000,
|
152
|
+
CBF_SKIP_CONNECT_CONFIRMS = 0x00040000,
|
153
|
+
CBF_SKIP_REGISTRATIONS = 0x00080000,
|
154
|
+
CBF_SKIP_UNREGISTRATIONS = 0x00100000,
|
155
|
+
CBF_SKIP_DISCONNECTS = 0x00200000,
|
156
|
+
CBF_SKIP_ALLNOTIFICATIONS = 0x003c0000,
|
157
|
+
MF_HSZ_INFO = 0x01000000,
|
158
|
+
MF_SENDMSGS = 0x02000000,
|
159
|
+
MF_POSTMSGS = 0x04000000,
|
160
|
+
MF_CALLBACKS = 0x08000000,
|
161
|
+
MF_ERRORS = 0x10000000,
|
162
|
+
MF_LINKS = 0x20000000,
|
163
|
+
MF_CONV = 0x40000000,
|
164
|
+
MF_MASK = 0xFF000000;
|
165
|
+
|
166
|
+
enum : UINT {
|
167
|
+
EC_ENABLEALL = 0,
|
168
|
+
EC_ENABLEONE = ST_BLOCKNEXT,
|
169
|
+
EC_DISABLE = ST_BLOCKED,
|
170
|
+
EC_QUERYWAITING = 2
|
171
|
+
}
|
172
|
+
|
173
|
+
enum : UINT {
|
174
|
+
DNS_REGISTER = 1,
|
175
|
+
DNS_UNREGISTER = 2,
|
176
|
+
DNS_FILTERON = 4,
|
177
|
+
DNS_FILTEROFF = 8
|
178
|
+
}
|
179
|
+
|
180
|
+
/+
|
181
|
+
#define HDATA_APPOWNED 1
|
182
|
+
#define MAX_MONITORS 4
|
183
|
+
+/
|
184
|
+
|
185
|
+
enum : int {
|
186
|
+
MH_CREATE = 1,
|
187
|
+
MH_KEEP = 2,
|
188
|
+
MH_DELETE = 3,
|
189
|
+
MH_CLEANUP = 4
|
190
|
+
}
|
191
|
+
|
192
|
+
alias HANDLE HCONVLIST, HCONV, HSZ, HDDEDATA;
|
193
|
+
|
194
|
+
extern (Windows) alias HDDEDATA
|
195
|
+
function(UINT, UINT, HCONV, HSZ, HSZ, HDDEDATA, DWORD, DWORD) PFNCALLBACK;
|
196
|
+
|
197
|
+
struct HSZPAIR {
|
198
|
+
HSZ hszSvc;
|
199
|
+
HSZ hszTopic;
|
200
|
+
}
|
201
|
+
alias HSZPAIR* PHSZPAIR;
|
202
|
+
|
203
|
+
struct CONVCONTEXT {
|
204
|
+
UINT cb = CONVCONTEXT.sizeof;
|
205
|
+
UINT wFlags;
|
206
|
+
UINT wCountryID;
|
207
|
+
int iCodePage;
|
208
|
+
DWORD dwLangID;
|
209
|
+
DWORD dwSecurity;
|
210
|
+
SECURITY_QUALITY_OF_SERVICE qos;
|
211
|
+
}
|
212
|
+
alias CONVCONTEXT* PCONVCONTEXT;
|
213
|
+
|
214
|
+
struct CONVINFO {
|
215
|
+
DWORD cb = CONVINFO.sizeof;
|
216
|
+
DWORD hUser;
|
217
|
+
HCONV hConvPartner;
|
218
|
+
HSZ hszSvcPartner;
|
219
|
+
HSZ hszServiceReq;
|
220
|
+
HSZ hszTopic;
|
221
|
+
HSZ hszItem;
|
222
|
+
UINT wFmt;
|
223
|
+
UINT wType;
|
224
|
+
UINT wStatus;
|
225
|
+
UINT wConvst;
|
226
|
+
UINT wLastError;
|
227
|
+
HCONVLIST hConvList;
|
228
|
+
CONVCONTEXT ConvCtxt;
|
229
|
+
HWND hwnd;
|
230
|
+
HWND hwndPartner;
|
231
|
+
}
|
232
|
+
alias CONVINFO* PCONVINFO;
|
233
|
+
|
234
|
+
struct DDEML_MSG_HOOK_DATA {
|
235
|
+
UINT uiLo;
|
236
|
+
UINT uiHi;
|
237
|
+
DWORD cbData;
|
238
|
+
DWORD[8] Data;
|
239
|
+
}
|
240
|
+
|
241
|
+
struct MONHSZSTRUCT {
|
242
|
+
UINT cb = MONHSZSTRUCT.sizeof;
|
243
|
+
int fsAction;
|
244
|
+
DWORD dwTime;
|
245
|
+
HSZ hsz;
|
246
|
+
HANDLE hTask;
|
247
|
+
TCHAR[1] _str;
|
248
|
+
|
249
|
+
TCHAR* str() { return _str.ptr; }
|
250
|
+
}
|
251
|
+
alias MONHSZSTRUCT* PMONHSZSTRUCT;
|
252
|
+
|
253
|
+
struct MONLINKSTRUCT {
|
254
|
+
UINT cb = MONLINKSTRUCT.sizeof;
|
255
|
+
DWORD dwTime;
|
256
|
+
HANDLE hTask;
|
257
|
+
BOOL fEstablished;
|
258
|
+
BOOL fNoData;
|
259
|
+
HSZ hszSvc;
|
260
|
+
HSZ hszTopic;
|
261
|
+
HSZ hszItem;
|
262
|
+
UINT wFmt;
|
263
|
+
BOOL fServer;
|
264
|
+
HCONV hConvServer;
|
265
|
+
HCONV hConvClient;
|
266
|
+
}
|
267
|
+
alias MONLINKSTRUCT* PMONLINKSTRUCT;
|
268
|
+
|
269
|
+
struct MONCONVSTRUCT {
|
270
|
+
UINT cb = MONCONVSTRUCT.sizeof;
|
271
|
+
BOOL fConnect;
|
272
|
+
DWORD dwTime;
|
273
|
+
HANDLE hTask;
|
274
|
+
HSZ hszSvc;
|
275
|
+
HSZ hszTopic;
|
276
|
+
HCONV hConvClient;
|
277
|
+
HCONV hConvServer;
|
278
|
+
}
|
279
|
+
alias MONCONVSTRUCT* PMONCONVSTRUCT;
|
280
|
+
|
281
|
+
struct MONCBSTRUCT {
|
282
|
+
UINT cb = MONCBSTRUCT.sizeof;
|
283
|
+
DWORD dwTime;
|
284
|
+
HANDLE hTask;
|
285
|
+
DWORD dwRet;
|
286
|
+
UINT wType;
|
287
|
+
UINT wFmt;
|
288
|
+
HCONV hConv;
|
289
|
+
HSZ hsz1;
|
290
|
+
HSZ hsz2;
|
291
|
+
HDDEDATA hData;
|
292
|
+
ULONG_PTR dwData1;
|
293
|
+
ULONG_PTR dwData2;
|
294
|
+
CONVCONTEXT cc;
|
295
|
+
DWORD cbData;
|
296
|
+
DWORD[8] Data;
|
297
|
+
}
|
298
|
+
alias MONCBSTRUCT* PMONCBSTRUCT;
|
299
|
+
|
300
|
+
struct MONERRSTRUCT {
|
301
|
+
UINT cb = MONERRSTRUCT.sizeof;
|
302
|
+
UINT wLastError;
|
303
|
+
DWORD dwTime;
|
304
|
+
HANDLE hTask;
|
305
|
+
}
|
306
|
+
alias MONERRSTRUCT* PMONERRSTRUCT;
|
307
|
+
|
308
|
+
struct MONMSGSTRUCT {
|
309
|
+
UINT cb = MONMSGSTRUCT.sizeof;
|
310
|
+
HWND hwndTo;
|
311
|
+
DWORD dwTime;
|
312
|
+
HANDLE hTask;
|
313
|
+
UINT wMsg;
|
314
|
+
WPARAM wParam;
|
315
|
+
LPARAM lParam;
|
316
|
+
DDEML_MSG_HOOK_DATA dmhd;
|
317
|
+
}
|
318
|
+
alias MONMSGSTRUCT* PMONMSGSTRUCT;
|
319
|
+
|
320
|
+
extern (Windows) {
|
321
|
+
BOOL DdeAbandonTransaction(DWORD, HCONV, DWORD);
|
322
|
+
PBYTE DdeAccessData(HDDEDATA, PDWORD);
|
323
|
+
HDDEDATA DdeAddData(HDDEDATA, PBYTE, DWORD, DWORD);
|
324
|
+
HDDEDATA DdeClientTransaction(PBYTE, DWORD, HCONV, HSZ, UINT, UINT,
|
325
|
+
DWORD, PDWORD);
|
326
|
+
int DdeCmpStringHandles(HSZ, HSZ);
|
327
|
+
HCONV DdeConnect(DWORD, HSZ, HSZ, PCONVCONTEXT);
|
328
|
+
HCONVLIST DdeConnectList(DWORD, HSZ, HSZ, HCONVLIST, PCONVCONTEXT);
|
329
|
+
HDDEDATA DdeCreateDataHandle(DWORD, PBYTE, DWORD, DWORD, HSZ, UINT,
|
330
|
+
UINT);
|
331
|
+
HSZ DdeCreateStringHandleA(DWORD, LPSTR, int);
|
332
|
+
HSZ DdeCreateStringHandleW(DWORD, LPWSTR, int);
|
333
|
+
BOOL DdeDisconnect(HCONV);
|
334
|
+
BOOL DdeDisconnectList(HCONVLIST);
|
335
|
+
BOOL DdeEnableCallback(DWORD, HCONV, UINT);
|
336
|
+
BOOL DdeFreeDataHandle(HDDEDATA);
|
337
|
+
BOOL DdeFreeStringHandle(DWORD, HSZ);
|
338
|
+
DWORD DdeGetData(HDDEDATA, PBYTE, DWORD, DWORD);
|
339
|
+
UINT DdeGetLastError(DWORD);
|
340
|
+
BOOL DdeImpersonateClient(HCONV);
|
341
|
+
UINT DdeInitializeA(PDWORD, PFNCALLBACK, DWORD, DWORD);
|
342
|
+
UINT DdeInitializeW(PDWORD, PFNCALLBACK, DWORD, DWORD);
|
343
|
+
BOOL DdeKeepStringHandle(DWORD, HSZ);
|
344
|
+
HDDEDATA DdeNameService(DWORD, HSZ, HSZ, UINT);
|
345
|
+
BOOL DdePostAdvise(DWORD, HSZ, HSZ);
|
346
|
+
UINT DdeQueryConvInfo(HCONV, DWORD, PCONVINFO);
|
347
|
+
HCONV DdeQueryNextServer(HCONVLIST, HCONV);
|
348
|
+
DWORD DdeQueryStringA(DWORD, HSZ, LPSTR, DWORD, int);
|
349
|
+
DWORD DdeQueryStringW(DWORD, HSZ, LPWSTR, DWORD, int);
|
350
|
+
HCONV DdeReconnect(HCONV);
|
351
|
+
BOOL DdeSetUserHandle(HCONV, DWORD, DWORD);
|
352
|
+
BOOL DdeUnaccessData(HDDEDATA);
|
353
|
+
BOOL DdeUninitialize(DWORD);
|
354
|
+
}
|
355
|
+
|
356
|
+
const TCHAR[]
|
357
|
+
SZDDESYS_TOPIC = "System",
|
358
|
+
SZDDESYS_ITEM_TOPICS = "Topics",
|
359
|
+
SZDDESYS_ITEM_SYSITEMS = "SysItems",
|
360
|
+
SZDDESYS_ITEM_RTNMSG = "ReturnMessage",
|
361
|
+
SZDDESYS_ITEM_STATUS = "Status",
|
362
|
+
SZDDESYS_ITEM_FORMATS = "Formats",
|
363
|
+
SZDDESYS_ITEM_HELP = "Help",
|
364
|
+
SZDDE_ITEM_ITEMLIST = "TopicItemList";
|
365
|
+
|
366
|
+
version (Unicode) {
|
367
|
+
alias DdeCreateStringHandleW DdeCreateStringHandle;
|
368
|
+
alias DdeInitializeW DdeInitialize;
|
369
|
+
alias DdeQueryStringW DdeQueryString;
|
370
|
+
} else {
|
371
|
+
alias DdeCreateStringHandleA DdeCreateStringHandle;
|
372
|
+
alias DdeInitializeA DdeInitialize;
|
373
|
+
alias DdeQueryStringA DdeQueryString;
|
374
|
+
}
|
data/doc/types.txt
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
== Parameters ( win32-api )
|
2
|
+
Long: 'L'
|
3
|
+
Integer: 'I'
|
4
|
+
Pointer: 'P'
|
5
|
+
Void: 'V'
|
6
|
+
String: 'S'
|
7
|
+
Callback: 'K' # win32-api only
|
8
|
+
|
9
|
+
== Windows Data Types
|
10
|
+
BOOL => 'I' (or 'B', win32-api only)
|
11
|
+
DWORD => 'L'
|
12
|
+
HANDLE => 'L'
|
13
|
+
LPDWORD => 'P' (or 'L')
|
14
|
+
LPTSTR => 'P'
|
15
|
+
LPCTSTR => 'S'
|
16
|
+
UINT => 'L'
|
17
|
+
VOID => 'V'
|
18
|
+
WORD => 'I'
|
19
|
+
LPVOID => 'L' (or 'P')
|
20
|
+
CALLBACK => 'K'
|
21
|
+
|
22
|
+
== C Data Types
|
23
|
+
void => 'V'
|
24
|
+
void* => 'P'
|
25
|
+
char* => 'P'
|
26
|
+
const char* => 'L'
|
27
|
+
int => 'I'
|
28
|
+
long => 'L'
|
29
|
+
struct => 'P'
|
30
|
+
struct* => 'P'
|
31
|
+
|
32
|
+
== Notes
|
33
|
+
In practice most LPVOID types should be designated as 'L' because this
|
34
|
+
usually means the function is looking for an address. Check the documentation
|
35
|
+
for details.
|
36
|
+
|
37
|
+
If using the windows-api library, you can use 'B' instead of 'I' for the return
|
38
|
+
type for functions that return a BOOL.
|
39
|
+
|
40
|
+
===== Definitions from windows-api (api.rb)
|
41
|
+
# Verbose data types that can be used instead of single letters
|
42
|
+
DATA_TYPES = {
|
43
|
+
'ATOM' => 'I',
|
44
|
+
'BOOL' => 'B',
|
45
|
+
'BOOLEAN' => 'B',
|
46
|
+
'BYTE' => 'I',
|
47
|
+
'CALLBACK' => 'K',
|
48
|
+
'CHAR' => 'I',
|
49
|
+
'COLORREF' => 'L',
|
50
|
+
'DWORD' => 'L',
|
51
|
+
'DWORDLONG' => 'L',
|
52
|
+
'DWORD_PTR' => 'P',
|
53
|
+
'DWORD32' => 'I',
|
54
|
+
'DWORD64' => 'L',
|
55
|
+
'HACCEL' => 'L',
|
56
|
+
'HANDLE' => 'L',
|
57
|
+
'HBITMAP' => 'L',
|
58
|
+
'HBRUSH' => 'L',
|
59
|
+
'HCOLORSPACE' => 'L',
|
60
|
+
'HCONV' => 'L',
|
61
|
+
'HDC' => 'L',
|
62
|
+
'HFILE' => 'I',
|
63
|
+
'HKEY' => 'L',
|
64
|
+
'HFONT' => 'L',
|
65
|
+
'HINSTANCE' => 'L',
|
66
|
+
'HKEY' => 'L',
|
67
|
+
'HLOCAL' => 'L',
|
68
|
+
'HMENU' => 'L',
|
69
|
+
'HMODULE' => 'L',
|
70
|
+
'HRESULT' => 'L',
|
71
|
+
'HWND' => 'L',
|
72
|
+
'INT' => 'I',
|
73
|
+
'INT_PTR' => 'P',
|
74
|
+
'INT32' => 'I',
|
75
|
+
'INT64' => 'L',
|
76
|
+
'LANGID' => 'I',
|
77
|
+
'LCID' => 'L',
|
78
|
+
'LCTYPE' => 'L',
|
79
|
+
'LONG' => 'L',
|
80
|
+
'LONGLONG' => 'L',
|
81
|
+
'LONG_PTR' => 'P',
|
82
|
+
'LONG32' => 'L',
|
83
|
+
'LONG64' => 'L',
|
84
|
+
'LPARAM' => 'P',
|
85
|
+
'LPBOOL' => 'P',
|
86
|
+
'LPBYTE' => 'P',
|
87
|
+
'LPCOLORREF' => 'P',
|
88
|
+
'LPCSTR' => 'P',
|
89
|
+
'LPCTSTR' => 'P',
|
90
|
+
'LPCVOID' => 'L',
|
91
|
+
'LPCWSTR' => 'P',
|
92
|
+
'LPDWORD' => 'P',
|
93
|
+
'LPHANDLE' => 'P',
|
94
|
+
'LPINT' => 'P',
|
95
|
+
'LPLONG' => 'P',
|
96
|
+
'LPSTR' => 'P',
|
97
|
+
'LPTSTR' => 'P',
|
98
|
+
'LPVOID' => 'L',
|
99
|
+
'LPWORD' => 'P',
|
100
|
+
'LPWSTR' => 'P',
|
101
|
+
'LRESULT' => 'P',
|
102
|
+
'PBOOL' => 'P',
|
103
|
+
'PBOOLEAN' => 'P',
|
104
|
+
'PBYTE' => 'P',
|
105
|
+
'PHKEY' => 'P',
|
106
|
+
'SC_HANDLE' => 'L',
|
107
|
+
'SC_LOCK' => 'L',
|
108
|
+
'SERVICE_STATUS_HANDLE' => 'L',
|
109
|
+
'SHORT' => 'I',
|
110
|
+
'SIZE_T' => 'P',
|
111
|
+
'TCHAR' => 'L',
|
112
|
+
'UINT' => 'I',
|
113
|
+
'UINT_PTR' => 'P',
|
114
|
+
'UINT32' => 'I',
|
115
|
+
'UINT64' => 'L',
|
116
|
+
'ULONG' => 'L',
|
117
|
+
'ULONGLONG' => 'L',
|
118
|
+
'ULONG_PTR' => 'P',
|
119
|
+
'ULONG32' => 'L',
|
120
|
+
'ULONG64' => 'L',
|
121
|
+
'USHORT' => 'I',
|
122
|
+
'USN' => 'L',
|
123
|
+
'WINAPI' => 'L',
|
124
|
+
'WORD' => 'I'
|
125
|
+
}
|
126
|
+
|
127
|
+
Win::Library shortcuts (Mostly in line with Array#pack)
|
128
|
+
TYPES = {
|
129
|
+
V: :void, # For functions that return nothing (return type void).
|
130
|
+
v: :void, # For functions that return nothing (return type void).
|
131
|
+
C: :uchar, #– 8-bit unsigned character (byte)
|
132
|
+
c: :char, # 8-bit character (byte)
|
133
|
+
# :int8 – 8-bit signed integer
|
134
|
+
# :uint8 – 8-bit unsigned integer
|
135
|
+
S: :ushort, # – 16-bit unsigned integer (Win32API: used for string)
|
136
|
+
s: :short, # – 16-bit signed integer
|
137
|
+
# :uint16 – 16-bit unsigned integer
|
138
|
+
# :int16 – 16-bit signed integer
|
139
|
+
I: :uint, # 32-bit unsigned integer
|
140
|
+
i: :int, # 32-bit signed integer
|
141
|
+
# :uint32 – 32-bit unsigned integer
|
142
|
+
# :int32 – 32-bit signed integer
|
143
|
+
L: :ulong, # unsigned long int – platform-specific size
|
144
|
+
l: :long, # long int – platform-specific size (http://groups.google.com/group/ruby-ffi/browse_thread/thread/4762fc77130339b1)
|
145
|
+
# :int64 – 64-bit signed integer
|
146
|
+
# :uint64 – 64-bit unsigned integer
|
147
|
+
# :long_long – 64-bit signed integer
|
148
|
+
# :ulong_long – 64-bit unsigned integer
|
149
|
+
F: :float, # 32-bit floating point
|
150
|
+
D: :double, # 64-bit floating point (double-precision)
|
151
|
+
P: :pointer, # pointer – platform-specific size
|
152
|
+
p: :string, # C-style (NULL-terminated) character string (Win32API: S)
|
153
|
+
B: :bool # (?? 1 byte in C++)
|
154
|
+
#For function argument type only:
|
155
|
+
# :buffer_in – Similar to :pointer, but optimized for Buffers that the function can only read (not write).
|
156
|
+
# :buffer_out – Similar to :pointer, but optimized for Buffers that the function can only write (not read).
|
157
|
+
# :buffer_inout – Similar to :pointer, but may be optimized for Buffers.
|
158
|
+
# :varargs – Variable arguments
|
159
|
+
}
|
Binary file
|
data/doc/~$e_formats.doc
ADDED
Binary file
|
File without changes
|
data/lib/dde/app.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
module DDE
|
2
|
+
|
3
|
+
module Errors # :nodoc:
|
4
|
+
def self.[](error_code)
|
5
|
+
Win::DDE::ERRORS[error_code]
|
6
|
+
end
|
7
|
+
|
8
|
+
class InitError < RuntimeError # :nodoc:
|
9
|
+
end
|
10
|
+
class FormatError < RuntimeError # :nodoc:
|
11
|
+
end
|
12
|
+
class StringError < RuntimeError # :nodoc:
|
13
|
+
end
|
14
|
+
class ServiceError < RuntimeError # :nodoc:
|
15
|
+
end
|
16
|
+
class ClientError < RuntimeError # :nodoc:
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Class encapsulates DDE application. DDE::App serves as a base for more specific types,
|
21
|
+
# such as DDE::Server or DDE:: Client.
|
22
|
+
class App
|
23
|
+
include Win::DDE
|
24
|
+
|
25
|
+
attr_reader :id, :init_flags
|
26
|
+
|
27
|
+
# Creates new DDE application and starts DDE instance
|
28
|
+
# if dde_callback block is attached
|
29
|
+
def initialize( init_flags=nil, &dde_callback )
|
30
|
+
@init_flags = init_flags
|
31
|
+
|
32
|
+
start_dde init_flags, &dde_callback if dde_callback
|
33
|
+
|
34
|
+
# todo: Destructor to ensure Dde instance is uninitialized and string handles freed (is it even working?)
|
35
|
+
#ObjectSpace.define_finalizer self, ->(id) { stop_dde }
|
36
|
+
end
|
37
|
+
|
38
|
+
# (Re)Initialize application with DDEML library, providing attached dde callback
|
39
|
+
# either preserved @init_flags or init_flags argument are used
|
40
|
+
def start_dde( init_flags=nil, &dde_callback )
|
41
|
+
@init_flags = init_flags || @init_flags || APPCLASS_STANDARD
|
42
|
+
|
43
|
+
try "Starting DDE" do
|
44
|
+
@id, status = dde_initialize @id, @init_flags, &dde_callback
|
45
|
+
error(status) unless @id && status == DMLERR_NO_ERROR
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# (Re)Initialize application with DDEML library, providing attached dde callback
|
50
|
+
def stop_dde
|
51
|
+
try "Stopping DDE" do
|
52
|
+
error unless @id && dde_uninitialize(@id) # Uninitialize app with DDEML library
|
53
|
+
@id = nil # Clear instance id if uninitialization successful
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Expects a block, yields to it inside a rescue block, raises given error_type with extended fail message.
|
58
|
+
# Returns self in case of success (to enable method chaining).
|
59
|
+
def try( action, error_type=DDE::Errors::InitError )
|
60
|
+
begin
|
61
|
+
yield
|
62
|
+
rescue => e
|
63
|
+
raise error_type, action + " failed with: #{e}"
|
64
|
+
end
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Raises Runtime error with message based on given message (DdeGetLastError message if no message given)
|
69
|
+
def error( message = nil )
|
70
|
+
raise case message
|
71
|
+
when Integer
|
72
|
+
DDE::Errors[message]
|
73
|
+
when nil
|
74
|
+
DDE::Errors[dde_get_last_error(@id)]
|
75
|
+
else
|
76
|
+
message
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def dde_active?
|
81
|
+
!!@id
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
data/lib/dde/client.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module DDE
|
2
|
+
|
3
|
+
# Class encapsulates DDE Client that requests connection with DDE server and exchanges data with it via DDE
|
4
|
+
class Client < App
|
5
|
+
|
6
|
+
attr_reader :conversation, # active DDE conversation that client is engaged in
|
7
|
+
:service, #service that the client is connected to
|
8
|
+
:topic, # active DDE conversation topic
|
9
|
+
:item # active DDE conversation item
|
10
|
+
|
11
|
+
# # Creates new DDE client instance
|
12
|
+
# def initialize(init_flags = nil, &dde_callback )
|
13
|
+
# super init_flags, &dde_callback
|
14
|
+
# end
|
15
|
+
|
16
|
+
# establish a conversation with a server application that supports the specified service
|
17
|
+
# name and topic name pair.
|
18
|
+
def start_conversation( service=nil, topic=nil )
|
19
|
+
try "Starting conversation #{service} #{topic}", DDE::Errors::ClientError do
|
20
|
+
error "DDE is not initialized" unless dde_active?
|
21
|
+
error "Another conversation already established" if conversation_active?
|
22
|
+
|
23
|
+
# Create DDE strings for service and topic unless they are omitted
|
24
|
+
@service = DDE::DdeString.new(@id, service) if service
|
25
|
+
@topic = DDE::DdeString.new(@id, topic) if topic
|
26
|
+
|
27
|
+
# Initiate new DDE conversation, returns conversation handle or nil
|
28
|
+
error unless @conversation = dde_connect(@id, @service.handle, @topic.handle)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def stop_conversation
|
33
|
+
try "Stopping conversation", DDE::Errors::ClientError do
|
34
|
+
error "DDE not started" unless dde_active?
|
35
|
+
error "Conversation not started" unless conversation_active?
|
36
|
+
|
37
|
+
error unless dde_disconnect(@conversation) && # Stop DDE conversation
|
38
|
+
dde_free_string_handle(@id, @service.handle) && # Free string handles for service name
|
39
|
+
dde_free_string_handle(@id, @topic.handle) # Free string handles for topic name
|
40
|
+
|
41
|
+
# Unset attributes for conversation, service and topic
|
42
|
+
@conversation = nil
|
43
|
+
@service = nil
|
44
|
+
@topic = nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def conversation_active?
|
49
|
+
!!@conversation
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|