dde 0.2.9 → 0.2.11
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 -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
|