dde 0.2.9 → 0.2.11
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -5
- data/.gitignore +21 -22
- data/LICENSE +20 -20
- data/README.rdoc +19 -19
- data/Rakefile +60 -60
- data/VERSION +1 -1
- data/bin/dde_main +32 -32
- data/dde.gemspec +99 -99
- data/doc/ddeml.d.txt +374 -374
- data/doc/types.txt +159 -159
- data/exp/exp_client.rb +44 -44
- data/exp/exp_dde_monitor.rb +18 -18
- data/exp/exp_dde_server.rb +36 -36
- data/exp/exp_lib.rb +38 -38
- data/exp/exp_server.rb +44 -44
- data/features/dde.feature +9 -9
- data/features/support/env.rb +4 -4
- data/lib/dde.rb +9 -9
- data/lib/dde/app.rb +91 -91
- data/lib/dde/client.rb +102 -102
- data/lib/dde/dde_string.rb +32 -32
- data/lib/dde/monitor.rb +93 -93
- data/lib/dde/server.rb +40 -40
- data/lib/dde/xl_server.rb +89 -89
- data/lib/dde/xl_table.rb +190 -190
- data/spec/dde/app_shared.rb +84 -84
- data/spec/dde/app_spec.rb +7 -7
- data/spec/dde/client_spec.rb +199 -199
- data/spec/dde/dde_string_spec.rb +39 -39
- data/spec/dde/monitor_spec.rb +33 -33
- data/spec/dde/server_shared.rb +91 -91
- data/spec/dde/server_spec.rb +36 -36
- data/spec/dde/xl_server_spec.rb +63 -63
- data/spec/dde/xl_table_spec.rb +50 -50
- data/spec/spec.opts +2 -2
- data/spec/spec_helper.rb +21 -25
- metadata +35 -18
data/lib/dde/dde_string.rb
CHANGED
@@ -1,33 +1,33 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
# Class encapsulates DDE string. In addition to normal string behavior,
|
4
|
-
# it also has *handle* that can be passed to dde functions
|
5
|
-
class DdeString < String
|
6
|
-
include Win::
|
7
|
-
|
8
|
-
attr_accessor :handle, # string handle passable to DDEML functions
|
9
|
-
:instance_id, # instance id of DDE app that created this DdeString
|
10
|
-
:code_page, # Windows code page for this string (CP_WINANSI or CP_WINUNICODE)
|
11
|
-
:name # ORIGINAL string used to create this DdeString
|
12
|
-
|
13
|
-
# Given the DDE application instance_id, you cane create DdeStrings
|
14
|
-
# either from regular string or from known DdeString handle
|
15
|
-
def initialize(instance_id, string_or_handle, code_page=CP_WINANSI)
|
16
|
-
@instance_id = instance_id
|
17
|
-
@code_page = code_page
|
18
|
-
|
19
|
-
begin
|
20
|
-
if string_or_handle.is_a? String
|
21
|
-
@name = string_or_handle
|
22
|
-
error unless @handle = dde_create_string_handle(@instance_id, @name, @code_page)
|
23
|
-
else
|
24
|
-
@handle = string_or_handle
|
25
|
-
error unless @name = dde_query_string(@instance_id, @handle, @code_page)
|
26
|
-
end
|
27
|
-
rescue => e
|
28
|
-
end
|
29
|
-
raise
|
30
|
-
super @name
|
31
|
-
end
|
32
|
-
end
|
1
|
+
module Dde
|
2
|
+
|
3
|
+
# Class encapsulates DDE string. In addition to normal string behavior,
|
4
|
+
# it also has *handle* that can be passed to dde functions
|
5
|
+
class DdeString < String
|
6
|
+
include Win::Dde
|
7
|
+
|
8
|
+
attr_accessor :handle, # string handle passable to DDEML functions
|
9
|
+
:instance_id, # instance id of DDE app that created this DdeString
|
10
|
+
:code_page, # Windows code page for this string (CP_WINANSI or CP_WINUNICODE)
|
11
|
+
:name # ORIGINAL string used to create this DdeString
|
12
|
+
|
13
|
+
# Given the DDE application instance_id, you cane create DdeStrings
|
14
|
+
# either from regular string or from known DdeString handle
|
15
|
+
def initialize(instance_id, string_or_handle, code_page=CP_WINANSI)
|
16
|
+
@instance_id = instance_id
|
17
|
+
@code_page = code_page
|
18
|
+
|
19
|
+
begin
|
20
|
+
if string_or_handle.is_a? String
|
21
|
+
@name = string_or_handle
|
22
|
+
error unless @handle = dde_create_string_handle(@instance_id, @name, @code_page)
|
23
|
+
else
|
24
|
+
@handle = string_or_handle
|
25
|
+
error unless @name = dde_query_string(@instance_id, @handle, @code_page)
|
26
|
+
end
|
27
|
+
rescue => e
|
28
|
+
end
|
29
|
+
raise Dde::Errors::StringError, "Failed to initialize DDE string: #{e} #{e.backtrace.join("\n")}" unless @handle && @name && !e
|
30
|
+
super @name
|
31
|
+
end
|
32
|
+
end
|
33
33
|
end
|
data/lib/dde/monitor.rb
CHANGED
@@ -1,94 +1,94 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
# Class encapsulates DDE Monitor that prints all DDE transactions to console
|
4
|
-
class Monitor < App
|
5
|
-
|
6
|
-
attr_accessor :print, :calls
|
7
|
-
|
8
|
-
# Creates new DDE monitor instance
|
9
|
-
def initialize(init_flags=nil, print = nil, &callback)
|
10
|
-
init_flags ||=
|
11
|
-
APPCLASS_MONITOR | # this is monitor
|
12
|
-
MF_CALLBACKS | # monitor callback functions
|
13
|
-
MF_CONV | # monitor conversation data
|
14
|
-
MF_ERRORS | # monitor DDEML errors
|
15
|
-
MF_HSZ_INFO | # monitor data handle activity
|
16
|
-
MF_LINKS | # monitor advise loops
|
17
|
-
MF_POSTMSGS | # monitor posted DDE messages
|
18
|
-
MF_SENDMSGS # monitor sent DDE messages
|
19
|
-
|
20
|
-
@print = print
|
21
|
-
@calls = []
|
22
|
-
|
23
|
-
callback ||= lambda do |*args|
|
24
|
-
time = Time.now.strftime('%T.%6N')
|
25
|
-
values = extract_values(*args)
|
26
|
-
@calls << [time, values]
|
27
|
-
puts "#{time} #{values}" if @print
|
28
|
-
DDE_FACK
|
29
|
-
end
|
30
|
-
|
31
|
-
super init_flags, &callback
|
32
|
-
end
|
33
|
-
|
34
|
-
def extract_values(*args)
|
35
|
-
values = args.map {|arg| interprete_value(arg)}
|
36
|
-
|
37
|
-
# if this is a MONITOR transaction, extract hdata using the DdeAccessData
|
38
|
-
if values.first == :XTYP_MONITOR
|
39
|
-
data_type = case values.last
|
40
|
-
when :MF_CALLBACKS
|
41
|
-
MonCbStruct #.new(dde_get_data(args[5]).first)
|
42
|
-
# cb:: Specifies the structure's size, in bytes.
|
43
|
-
# dwTime:: Specifies the Windows time at which the transaction occurred. Windows time is the number of
|
44
|
-
# milliseconds that have elapsed since the system was booted.
|
45
|
-
# hTask:: Handle to the task (app instance) containing the DDE callback function that received the transaction.
|
46
|
-
# dwRet:: Specifies the value returned by the DDE callback function that processed the transaction.
|
47
|
-
# wType:: Specifies the transaction type.
|
48
|
-
# wFmt:: Specifies the format of the data exchanged (if any) during the transaction.
|
49
|
-
# hConv:: Handle to the conversation in which the transaction took place.
|
50
|
-
# hsz1:: Handle to a string.
|
51
|
-
# hsz2:: Handle to a string.
|
52
|
-
# hData:: Handle to the data exchanged (if any) during the transaction.
|
53
|
-
# dwData1:: Specifies additional data.
|
54
|
-
# dwData2:: Specifies additional data.
|
55
|
-
# cc:: Specifies a CONVCONTEXT structure containing language information used to share data in different languages.
|
56
|
-
# cbData:: Specifies the amount, in bytes, of data being passed with the transaction. This value can be
|
57
|
-
# more than 32 bytes.
|
58
|
-
# Data:: Contains the first 32 bytes of data being passed with the transaction (8 * sizeof(DWORD)).
|
59
|
-
|
60
|
-
when :MF_CONV
|
61
|
-
MonConvStruct
|
62
|
-
when :MF_ERRORS
|
63
|
-
MonErrStruct
|
64
|
-
when :MF_HSZ_INFO
|
65
|
-
MonHszStruct
|
66
|
-
when :MF_LINKS
|
67
|
-
MonLinksStruct
|
68
|
-
else
|
69
|
-
MonMsgStruct
|
70
|
-
end
|
71
|
-
|
72
|
-
#casting DDE data pointer into appropriate struct type
|
73
|
-
struct_pointer, size = dde_get_data(args[5])
|
74
|
-
data = data_type.new(struct_pointer)
|
75
|
-
|
76
|
-
values = [values.first, values.last] + data.members.map do |member|
|
77
|
-
value = data[member] rescue 'plonk'
|
78
|
-
"#{member}: #{interprete_value(value)}"
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
values
|
83
|
-
end
|
84
|
-
|
85
|
-
def interprete_value(arg)
|
86
|
-
return arg unless arg.kind_of? Fixnum rescue return 'plAnk'
|
87
|
-
return 0 if arg == 0
|
88
|
-
#Trying to interpete arg as a DDE string
|
89
|
-
dde_query_string(@id, arg)\
|
90
|
-
|| Win::
|
91
|
-
|| arg
|
92
|
-
end
|
93
|
-
end
|
1
|
+
module Dde
|
2
|
+
|
3
|
+
# Class encapsulates DDE Monitor that prints all DDE transactions to console
|
4
|
+
class Monitor < App
|
5
|
+
|
6
|
+
attr_accessor :print, :calls
|
7
|
+
|
8
|
+
# Creates new DDE monitor instance
|
9
|
+
def initialize(init_flags=nil, print = nil, &callback)
|
10
|
+
init_flags ||=
|
11
|
+
APPCLASS_MONITOR | # this is monitor
|
12
|
+
MF_CALLBACKS | # monitor callback functions
|
13
|
+
MF_CONV | # monitor conversation data
|
14
|
+
MF_ERRORS | # monitor DDEML errors
|
15
|
+
MF_HSZ_INFO | # monitor data handle activity
|
16
|
+
MF_LINKS | # monitor advise loops
|
17
|
+
MF_POSTMSGS | # monitor posted DDE messages
|
18
|
+
MF_SENDMSGS # monitor sent DDE messages
|
19
|
+
|
20
|
+
@print = print
|
21
|
+
@calls = []
|
22
|
+
|
23
|
+
callback ||= lambda do |*args|
|
24
|
+
time = Time.now.strftime('%T.%6N')
|
25
|
+
values = extract_values(*args)
|
26
|
+
@calls << [time, values]
|
27
|
+
puts "#{time} #{values}" if @print
|
28
|
+
DDE_FACK
|
29
|
+
end
|
30
|
+
|
31
|
+
super init_flags, &callback
|
32
|
+
end
|
33
|
+
|
34
|
+
def extract_values(*args)
|
35
|
+
values = args.map {|arg| interprete_value(arg)}
|
36
|
+
|
37
|
+
# if this is a MONITOR transaction, extract hdata using the DdeAccessData
|
38
|
+
if values.first == :XTYP_MONITOR
|
39
|
+
data_type = case values.last
|
40
|
+
when :MF_CALLBACKS
|
41
|
+
MonCbStruct #.new(dde_get_data(args[5]).first)
|
42
|
+
# cb:: Specifies the structure's size, in bytes.
|
43
|
+
# dwTime:: Specifies the Windows time at which the transaction occurred. Windows time is the number of
|
44
|
+
# milliseconds that have elapsed since the system was booted.
|
45
|
+
# hTask:: Handle to the task (app instance) containing the DDE callback function that received the transaction.
|
46
|
+
# dwRet:: Specifies the value returned by the DDE callback function that processed the transaction.
|
47
|
+
# wType:: Specifies the transaction type.
|
48
|
+
# wFmt:: Specifies the format of the data exchanged (if any) during the transaction.
|
49
|
+
# hConv:: Handle to the conversation in which the transaction took place.
|
50
|
+
# hsz1:: Handle to a string.
|
51
|
+
# hsz2:: Handle to a string.
|
52
|
+
# hData:: Handle to the data exchanged (if any) during the transaction.
|
53
|
+
# dwData1:: Specifies additional data.
|
54
|
+
# dwData2:: Specifies additional data.
|
55
|
+
# cc:: Specifies a CONVCONTEXT structure containing language information used to share data in different languages.
|
56
|
+
# cbData:: Specifies the amount, in bytes, of data being passed with the transaction. This value can be
|
57
|
+
# more than 32 bytes.
|
58
|
+
# Data:: Contains the first 32 bytes of data being passed with the transaction (8 * sizeof(DWORD)).
|
59
|
+
|
60
|
+
when :MF_CONV
|
61
|
+
MonConvStruct
|
62
|
+
when :MF_ERRORS
|
63
|
+
MonErrStruct
|
64
|
+
when :MF_HSZ_INFO
|
65
|
+
MonHszStruct
|
66
|
+
when :MF_LINKS
|
67
|
+
MonLinksStruct
|
68
|
+
else
|
69
|
+
MonMsgStruct
|
70
|
+
end
|
71
|
+
|
72
|
+
#casting DDE data pointer into appropriate struct type
|
73
|
+
struct_pointer, size = dde_get_data(args[5])
|
74
|
+
data = data_type.new(struct_pointer)
|
75
|
+
|
76
|
+
values = [values.first, values.last] + data.members.map do |member|
|
77
|
+
value = data[member] rescue 'plonk'
|
78
|
+
"#{member}: #{interprete_value(value)}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
values
|
83
|
+
end
|
84
|
+
|
85
|
+
def interprete_value(arg)
|
86
|
+
return arg unless arg.kind_of? Fixnum rescue return 'plAnk'
|
87
|
+
return 0 if arg == 0
|
88
|
+
#Trying to interpete arg as a DDE string
|
89
|
+
dde_query_string(@id, arg)\
|
90
|
+
|| Win::Dde.constants(false).inject(nil) {|res, const| arg == Win::Dde.const_get(const) ? res || const : res }\
|
91
|
+
|| arg
|
92
|
+
end
|
93
|
+
end
|
94
94
|
end
|
data/lib/dde/server.rb
CHANGED
@@ -1,41 +1,41 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
# Class encapsulates DDE Server with basic functionality (starting/stopping named service)
|
4
|
-
class Server < App
|
5
|
-
|
6
|
-
attr_reader :service # service(s) that this Server supports
|
7
|
-
|
8
|
-
def start_service( name, init_flags=nil, &dde_callback )
|
9
|
-
try "Starting service #{name}",
|
10
|
-
# Trying to start DDE if it was inactive
|
11
|
-
error unless dde_active? || start_dde( init_flags, &dde_callback )
|
12
|
-
|
13
|
-
# Create DDE string for name (this creates handle that can be passed to DDEML functions)
|
14
|
-
@service =
|
15
|
-
|
16
|
-
# Register new DDE service, returns true/false success code
|
17
|
-
error unless dde_name_service(@id, @service.handle, DNS_REGISTER)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def stop_service
|
22
|
-
try "Stopping active service",
|
23
|
-
error "Either DDE or service not initialized" unless dde_active? && service_active?
|
24
|
-
|
25
|
-
# Unregister DDE service, returns true/false success code
|
26
|
-
error unless dde_name_service(@id, @service.handle, DNS_UNREGISTER);
|
27
|
-
|
28
|
-
# Free string handle for service name
|
29
|
-
error unless dde_free_string_handle(@id, @service.handle)
|
30
|
-
|
31
|
-
# Clear handle if service successfuly stopped
|
32
|
-
@service = nil
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def service_active?
|
37
|
-
!!@service
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
1
|
+
module Dde
|
2
|
+
|
3
|
+
# Class encapsulates DDE Server with basic functionality (starting/stopping named service)
|
4
|
+
class Server < App
|
5
|
+
|
6
|
+
attr_reader :service # service(s) that this Server supports
|
7
|
+
|
8
|
+
def start_service( name, init_flags=nil, &dde_callback )
|
9
|
+
try "Starting service #{name}", Dde::Errors::ServiceError do
|
10
|
+
# Trying to start DDE if it was inactive
|
11
|
+
error unless dde_active? || start_dde( init_flags, &dde_callback )
|
12
|
+
|
13
|
+
# Create DDE string for name (this creates handle that can be passed to DDEML functions)
|
14
|
+
@service = Dde::DdeString.new(@id, name)
|
15
|
+
|
16
|
+
# Register new DDE service, returns true/false success code
|
17
|
+
error unless dde_name_service(@id, @service.handle, DNS_REGISTER)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def stop_service
|
22
|
+
try "Stopping active service", Dde::Errors::ServiceError do
|
23
|
+
error "Either DDE or service not initialized" unless dde_active? && service_active?
|
24
|
+
|
25
|
+
# Unregister DDE service, returns true/false success code
|
26
|
+
error unless dde_name_service(@id, @service.handle, DNS_UNREGISTER);
|
27
|
+
|
28
|
+
# Free string handle for service name
|
29
|
+
error unless dde_free_string_handle(@id, @service.handle)
|
30
|
+
|
31
|
+
# Clear handle if service successfuly stopped
|
32
|
+
@service = nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def service_active?
|
37
|
+
!!@service
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
41
|
end
|
data/lib/dde/xl_server.rb
CHANGED
@@ -1,90 +1,90 @@
|
|
1
|
-
require 'dde/xl_table'
|
2
|
-
|
3
|
-
module
|
4
|
-
|
5
|
-
# Class encapsulates DDE Server mimicking Excel. It is used to create DDE server with specific service name
|
6
|
-
# (default name 'excel') and store data received by the server via DDE
|
7
|
-
class XlServer < Server
|
8
|
-
|
9
|
-
attr_reader :format, # data format(s) (registered clipboard formats) that server supports
|
10
|
-
:data # data storage/processor
|
11
|
-
|
12
|
-
attr_accessor :actions # Actions to be run on table after each successful DDE input (:draw, :debug, :timer)
|
13
|
-
|
14
|
-
# Creates new Xl Server instance
|
15
|
-
def initialize(init_flags = nil, &dde_callback )
|
16
|
-
|
17
|
-
@data =
|
18
|
-
|
19
|
-
# Trying to register or retrieve existing format XlTable
|
20
|
-
try 'Registering format XlTable',
|
21
|
-
@format = register_clipboard_format("XlTable")
|
22
|
-
end
|
23
|
-
|
24
|
-
super init_flags, &dde_callback
|
25
|
-
end
|
26
|
-
|
27
|
-
# HDDEDATA CALLBACK DdeCallback(UINT uType, UINT uFmt, HCONV hConv, HSZ hsz1, HSZ hsz2,
|
28
|
-
# HDDEDATA hData, DWORD dwData1, DWORD dwData2)
|
29
|
-
def default_callback
|
30
|
-
lambda do |type, format, conv, hsz1, hsz2, data_handle, data1, data2|
|
31
|
-
case type
|
32
|
-
when XTYP_CONNECT # Request to connect from client, creating data exchange channel
|
33
|
-
# format:: Not used.
|
34
|
-
# conv:: Not used.
|
35
|
-
# hsz1:: Handle to the topic name.
|
36
|
-
# hsz2:: Handle to the service name.
|
37
|
-
# data_handle:: Handle to DDE data. Meaning depends on the type of the current transaction.
|
38
|
-
# data1:: Pointer to a CONVCONTEXT structure that contains context information for the conversation.
|
39
|
-
# If the client is not a DDEML application, this parameter is 0.
|
40
|
-
# data2:: Specifies whether the client is the same application instance as the server. If the parameter
|
41
|
-
# is 1, the client is the same instance. If it is 0, the client is a different instance.
|
42
|
-
# *Returns*:: A server callback function should return TRUE(1, but DDE_FACK works just fine too)
|
43
|
-
# to allow the client to establish a conversation on the specified service name and topic
|
44
|
-
# name pair, or the function should return FALSE to deny the conversation. If the callback
|
45
|
-
# function returns TRUE and a conversation is successfully established, the system passes
|
46
|
-
# the conversation handle to the server by issuing an XTYP_CONNECT_CONFIRM transaction to
|
47
|
-
# the server's callback function (unless the server specified the CBF_SKIP_CONNECT_CONFIRMS
|
48
|
-
# flag in the DdeInitialize function).
|
49
|
-
|
50
|
-
if hsz2 == @service.handle
|
51
|
-
cout "Service #{@service}: connect requested by client\n"
|
52
|
-
DDE_FACK # instead of true # Yes, this server supports requested (name) handle
|
53
|
-
else
|
54
|
-
cout "Service #{@service} unable to process connection request for #{hsz2}\n"
|
55
|
-
DDE_FNOTPROCESSED # 0 instead of false # No, server does not support requested (name) handle
|
56
|
-
end
|
57
|
-
|
58
|
-
when XTYP_POKE # Client initiated XTYP_POKE transaction to push unsolicited data to the server
|
59
|
-
# format:: Specifies the format of the data sent from the server.
|
60
|
-
# conv:: Handle to the conversation.
|
61
|
-
# hsz1:: Handle to the topic name. (Excel: [topic]item ?!)
|
62
|
-
# hsz2:: Handle to the item name.
|
63
|
-
# data_handle:: Handle to the data that the client is sending to the server.
|
64
|
-
# *Returns*:: A server callback function should return the DDE_FACK flag if it processes this
|
65
|
-
# transaction, the DDE_FBUSY flag if it is too busy to process this transaction,
|
66
|
-
# or the DDE_FNOTPROCESSED flag if it rejects this transaction.
|
67
|
-
|
68
|
-
@data.topic = dde_query_string(@id, hsz1) # Convert hsz1 into "[topic]item" string and
|
69
|
-
if @data.receive(data_handle) # Receive incoming DDE data and process it
|
70
|
-
|
71
|
-
# Perform actions like :draw, :debug, :timer, :formats on received data (default :timer)
|
72
|
-
@actions.each{|action| @data.send(action.to_sym)}
|
73
|
-
DDE_FACK # Transaction successful
|
74
|
-
else
|
75
|
-
@data.debug
|
76
|
-
cout "Service #{@service} unable to process data request (XTYP_POKE) for #{hsz2}"
|
77
|
-
DDE_FNOTPROCESSED # 0 Transaction NOT successful - return (HDDEDATA)TRUE; ?!(why TRUE, not FALSE)
|
78
|
-
end
|
79
|
-
else
|
80
|
-
DDE_FNOTPROCESSED # 0 - return((HDDEDATA)NULL);// is it the same as 0 ?!
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def start_service( name='excel', init_flags=nil, &dde_callback)
|
86
|
-
super name, init_flags, &dde_callback || default_callback
|
87
|
-
end
|
88
|
-
|
89
|
-
end
|
1
|
+
require 'dde/xl_table'
|
2
|
+
|
3
|
+
module Dde
|
4
|
+
|
5
|
+
# Class encapsulates DDE Server mimicking Excel. It is used to create DDE server with specific service name
|
6
|
+
# (default name 'excel') and store data received by the server via DDE
|
7
|
+
class XlServer < Server
|
8
|
+
|
9
|
+
attr_reader :format, # data format(s) (registered clipboard formats) that server supports
|
10
|
+
:data # data storage/processor
|
11
|
+
|
12
|
+
attr_accessor :actions # Actions to be run on table after each successful DDE input (:draw, :debug, :timer)
|
13
|
+
|
14
|
+
# Creates new Xl Server instance
|
15
|
+
def initialize(init_flags = nil, &dde_callback )
|
16
|
+
|
17
|
+
@data = Dde::XlTable.new
|
18
|
+
|
19
|
+
# Trying to register or retrieve existing format XlTable
|
20
|
+
try 'Registering format XlTable', Dde::Errors::FormatError do
|
21
|
+
@format = register_clipboard_format("XlTable")
|
22
|
+
end
|
23
|
+
|
24
|
+
super init_flags, &dde_callback
|
25
|
+
end
|
26
|
+
|
27
|
+
# HDDEDATA CALLBACK DdeCallback(UINT uType, UINT uFmt, HCONV hConv, HSZ hsz1, HSZ hsz2,
|
28
|
+
# HDDEDATA hData, DWORD dwData1, DWORD dwData2)
|
29
|
+
def default_callback
|
30
|
+
lambda do |type, format, conv, hsz1, hsz2, data_handle, data1, data2|
|
31
|
+
case type
|
32
|
+
when XTYP_CONNECT # Request to connect from client, creating data exchange channel
|
33
|
+
# format:: Not used.
|
34
|
+
# conv:: Not used.
|
35
|
+
# hsz1:: Handle to the topic name.
|
36
|
+
# hsz2:: Handle to the service name.
|
37
|
+
# data_handle:: Handle to DDE data. Meaning depends on the type of the current transaction.
|
38
|
+
# data1:: Pointer to a CONVCONTEXT structure that contains context information for the conversation.
|
39
|
+
# If the client is not a DDEML application, this parameter is 0.
|
40
|
+
# data2:: Specifies whether the client is the same application instance as the server. If the parameter
|
41
|
+
# is 1, the client is the same instance. If it is 0, the client is a different instance.
|
42
|
+
# *Returns*:: A server callback function should return TRUE(1, but DDE_FACK works just fine too)
|
43
|
+
# to allow the client to establish a conversation on the specified service name and topic
|
44
|
+
# name pair, or the function should return FALSE to deny the conversation. If the callback
|
45
|
+
# function returns TRUE and a conversation is successfully established, the system passes
|
46
|
+
# the conversation handle to the server by issuing an XTYP_CONNECT_CONFIRM transaction to
|
47
|
+
# the server's callback function (unless the server specified the CBF_SKIP_CONNECT_CONFIRMS
|
48
|
+
# flag in the DdeInitialize function).
|
49
|
+
|
50
|
+
if hsz2 == @service.handle
|
51
|
+
cout "Service #{@service}: connect requested by client\n"
|
52
|
+
DDE_FACK # instead of true # Yes, this server supports requested (name) handle
|
53
|
+
else
|
54
|
+
cout "Service #{@service} unable to process connection request for #{hsz2}\n"
|
55
|
+
DDE_FNOTPROCESSED # 0 instead of false # No, server does not support requested (name) handle
|
56
|
+
end
|
57
|
+
|
58
|
+
when XTYP_POKE # Client initiated XTYP_POKE transaction to push unsolicited data to the server
|
59
|
+
# format:: Specifies the format of the data sent from the server.
|
60
|
+
# conv:: Handle to the conversation.
|
61
|
+
# hsz1:: Handle to the topic name. (Excel: [topic]item ?!)
|
62
|
+
# hsz2:: Handle to the item name.
|
63
|
+
# data_handle:: Handle to the data that the client is sending to the server.
|
64
|
+
# *Returns*:: A server callback function should return the DDE_FACK flag if it processes this
|
65
|
+
# transaction, the DDE_FBUSY flag if it is too busy to process this transaction,
|
66
|
+
# or the DDE_FNOTPROCESSED flag if it rejects this transaction.
|
67
|
+
|
68
|
+
@data.topic = dde_query_string(@id, hsz1) # Convert hsz1 into "[topic]item" string and
|
69
|
+
if @data.receive(data_handle) # Receive incoming DDE data and process it
|
70
|
+
|
71
|
+
# Perform actions like :draw, :debug, :timer, :formats on received data (default :timer)
|
72
|
+
@actions.each{|action| @data.send(action.to_sym)}
|
73
|
+
DDE_FACK # Transaction successful
|
74
|
+
else
|
75
|
+
@data.debug
|
76
|
+
cout "Service #{@service} unable to process data request (XTYP_POKE) for #{hsz2}"
|
77
|
+
DDE_FNOTPROCESSED # 0 Transaction NOT successful - return (HDDEDATA)TRUE; ?!(why TRUE, not FALSE)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
DDE_FNOTPROCESSED # 0 - return((HDDEDATA)NULL);// is it the same as 0 ?!
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def start_service( name='excel', init_flags=nil, &dde_callback)
|
86
|
+
super name, init_flags, &dde_callback || default_callback
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
90
|
end
|