win 0.1.27 → 0.3.1
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 -21
- data/LICENSE +20 -20
- data/README.rdoc +175 -175
- data/Rakefile +58 -58
- data/VERSION +1 -1
- data/features/support/env.rb +4 -4
- data/features/win.feature +9 -9
- data/lib/win/dde.rb +1234 -1234
- data/lib/win/error.rb +1223 -1223
- data/lib/win/extensions.rb +41 -41
- data/lib/win/gui.rb +16 -16
- data/lib/win/gui/dialog.rb +50 -50
- data/lib/win/gui/input.rb +319 -319
- data/lib/win/gui/message.rb +807 -807
- data/lib/win/gui/window.rb +679 -679
- data/lib/win/library.rb +463 -463
- data/spec/spec.opts +2 -2
- data/spec/spec_helper.rb +140 -135
- data/spec/test_apps/locknote/LockNote.exe +0 -0
- data/spec/win/dde_spec.rb +528 -528
- data/spec/win/error_spec.rb +112 -112
- data/spec/win/extensions_spec.rb +73 -73
- data/spec/win/gui/dialog_spec.rb +43 -43
- data/spec/win/gui/input_spec.rb +101 -101
- data/spec/win/gui/message_spec.rb +236 -236
- data/spec/win/gui/window_spec.rb +549 -548
- data/spec/win/library_spec.rb +341 -341
- data/win.gemspec +87 -87
- metadata +34 -17
data/spec/spec.opts
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
--color
|
2
|
-
--format nested
|
1
|
+
--color
|
2
|
+
--format nested
|
data/spec/spec_helper.rb
CHANGED
@@ -1,136 +1,141 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
-
require 'spec'
|
4
|
-
require 'spec/autorun'
|
5
|
-
require 'win/gui'
|
6
|
-
|
7
|
-
$debug = true
|
8
|
-
|
9
|
-
# Customize RSpec with my own extensions
|
10
|
-
module ClassMacros
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
#
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'spec'
|
4
|
+
require 'spec/autorun'
|
5
|
+
require 'win/gui'
|
6
|
+
|
7
|
+
$debug = true
|
8
|
+
|
9
|
+
# Customize RSpec with my own extensions
|
10
|
+
module ClassMacros
|
11
|
+
|
12
|
+
# wrapper for it method that extracts description from example source code, such as:
|
13
|
+
# spec { use{ function(arg1 = 4, arg2 = 'string') }}
|
14
|
+
def spec &block
|
15
|
+
it description_from(caller[0]), &block # it description_from(*block.source_location), &block
|
16
|
+
#do lambda(&block).should_not raise_error end
|
17
|
+
end
|
18
|
+
|
19
|
+
# reads description line from source file and drops external brackets like its{}, use{}
|
20
|
+
# accepts as arguments either file name and line or call stack member (caller[0])
|
21
|
+
def description_from(*args)
|
22
|
+
case args.size
|
23
|
+
when 1
|
24
|
+
file, line = args.first.scan(/\A(.*?):(\d+)/).first
|
25
|
+
when 2
|
26
|
+
file, line = args
|
27
|
+
end
|
28
|
+
File.open(file) do |f|
|
29
|
+
f.lines.to_a[line.to_i-1].gsub( /(spec.*?{)|(use.*?{)|}/, '' ).strip
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Customize RSpec with my own extensions
|
35
|
+
module InstanceMacros
|
36
|
+
def use
|
37
|
+
lambda{yield}.should_not raise_error
|
38
|
+
end
|
39
|
+
|
40
|
+
def any_block
|
41
|
+
lambda{|*args| args}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Spec::Runner.configure do |config|
|
46
|
+
config.extend(ClassMacros)
|
47
|
+
config.include(InstanceMacros)
|
48
|
+
|
49
|
+
class << Spec::ExampleGroup
|
50
|
+
# def spoc &block
|
51
|
+
# it description_from(caller[0]), &block
|
52
|
+
# end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Global test methods
|
57
|
+
def cygwin?
|
58
|
+
@cygwin_flag ||= `ruby -v` =~ /cygwin/
|
59
|
+
end
|
60
|
+
|
61
|
+
def os
|
62
|
+
@os_flag ||= cygwin? ? `cmd /c ver` : `ver`
|
63
|
+
end
|
64
|
+
|
65
|
+
def vista?
|
66
|
+
os =~ /Version 6/
|
67
|
+
end
|
68
|
+
|
69
|
+
def xp?
|
70
|
+
os =~ /XP/
|
71
|
+
end
|
72
|
+
|
73
|
+
module WinTest
|
74
|
+
|
75
|
+
TEST_KEY_DELAY = 0.001
|
76
|
+
TEST_IMPOSSIBLE = 'Impossible'
|
77
|
+
TEST_CONVERSION_ERROR = /Can.t convert/
|
78
|
+
TEST_SLEEP_DELAY = 0.02
|
79
|
+
TEST_APP_PATH = File.join(File.dirname(__FILE__), "test_apps/locknote/LockNote.exe" )
|
80
|
+
TEST_APP_START = cygwin? ? "cmd /c start `cygpath -w #{TEST_APP_PATH}`" : 'start "" "' + TEST_APP_PATH + '"'
|
81
|
+
TEST_WIN_TITLE = 'LockNote - Steganos LockNote'
|
82
|
+
TEST_WIN_CLASS = 'ATL:00434098'
|
83
|
+
TEST_WIN_RECT = [710, 400, 1210, 800]
|
84
|
+
TEST_STATUSBAR_CLASS = 'msctls_statusbar32'
|
85
|
+
TEST_TEXTAREA_CLASS = 'ATL:00434310'
|
86
|
+
|
87
|
+
def any_handle
|
88
|
+
find_window(nil, nil)
|
89
|
+
end
|
90
|
+
|
91
|
+
def not_a_handle
|
92
|
+
123
|
93
|
+
end
|
94
|
+
|
95
|
+
def buffer
|
96
|
+
FFI::MemoryPointer.new(:char, 1024)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
module WinTestApp
|
101
|
+
|
102
|
+
include WinTest
|
103
|
+
include Win::Gui
|
104
|
+
#include Win::Gui::Convenience
|
105
|
+
|
106
|
+
def launch_test_app
|
107
|
+
system TEST_APP_START
|
108
|
+
sleep TEST_SLEEP_DELAY until (handle = find_window(nil, TEST_WIN_TITLE))
|
109
|
+
|
110
|
+
textarea = find_window_ex(handle, 0, TEST_TEXTAREA_CLASS, nil)
|
111
|
+
app = "Locknote" # App identifier
|
112
|
+
|
113
|
+
eigenklass = class << app; self; end # Extracting app's eigenclass
|
114
|
+
eigenklass.class_eval do # Defining singleton methods on app
|
115
|
+
define_method(:handle) {handle}
|
116
|
+
define_method(:textarea) {textarea}
|
117
|
+
end
|
118
|
+
|
119
|
+
@launched_test_app = app
|
120
|
+
end
|
121
|
+
|
122
|
+
def close_test_app(app = @launched_test_app)
|
123
|
+
while app && app.respond_to?( :handle) && find_window(nil, TEST_WIN_TITLE)
|
124
|
+
shut_window app.handle
|
125
|
+
sleep TEST_SLEEP_DELAY
|
126
|
+
end
|
127
|
+
@launched_test_app = nil
|
128
|
+
end
|
129
|
+
|
130
|
+
# Creates test app object and yields it back to the block
|
131
|
+
def test_app
|
132
|
+
app = launch_test_app
|
133
|
+
|
134
|
+
# def app.textarea #define singleton method retrieving app's text area
|
135
|
+
# Window::Window.new find_window_ex(self.handle, 0, TEST_TEXTAREA_CLASS, nil)
|
136
|
+
# end
|
137
|
+
|
138
|
+
yield app
|
139
|
+
close_test_app app
|
140
|
+
end
|
136
141
|
end
|
File without changes
|
data/spec/win/dde_spec.rb
CHANGED
@@ -1,529 +1,529 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
-
require 'win/dde'
|
3
|
-
|
4
|
-
module
|
5
|
-
include WinTest
|
6
|
-
include Win::
|
7
|
-
include Win::
|
8
|
-
|
9
|
-
POKE_STRING = "Poke_string"
|
10
|
-
|
11
|
-
def dde_cmd
|
12
|
-
APPCLASS_STANDARD
|
13
|
-
end
|
14
|
-
|
15
|
-
def dde_callback
|
16
|
-
->(*args){}
|
17
|
-
end
|
18
|
-
|
19
|
-
def zero_id
|
20
|
-
FFI::MemoryPointer.new(:long).write_long(0)
|
21
|
-
end
|
22
|
-
|
23
|
-
def buffer
|
24
|
-
FFI::MemoryPointer.new(:char, 1024)
|
25
|
-
end
|
26
|
-
|
27
|
-
def string_pointer
|
28
|
-
FFI::MemoryPointer.from_string("Pointer_string")
|
29
|
-
end
|
30
|
-
|
31
|
-
def extract_values(*args)
|
32
|
-
type, format, conv, hsz1, hsz2, data, data1, data2 = *args
|
33
|
-
@server_conv = conv
|
34
|
-
[Win::
|
35
|
-
dde_query_string(@client_id, hsz1),
|
36
|
-
dde_query_string(@client_id, hsz2),
|
37
|
-
data, data1, data2]
|
38
|
-
end
|
39
|
-
|
40
|
-
def setup_server(&server_block)
|
41
|
-
@client_calls = []
|
42
|
-
@server_calls = []
|
43
|
-
@client_id, st = dde_initialize(APPCLASS_STANDARD) {|*args| @client_calls << extract_values(*args); DDE_FACK}
|
44
|
-
@server_id, st = dde_initialize(APPCLASS_STANDARD,
|
45
|
-
&server_block || proc {|*args| @server_calls << extract_values(*args); DDE_FACK} )
|
46
|
-
@service_handle = dde_create_string_handle(@server_id, 'service 2', CP_WINANSI)
|
47
|
-
@topic_handle = dde_create_string_handle(@server_id, 'topic 2', CP_WINANSI)
|
48
|
-
dde_name_service(@server_id, @service_handle, DNS_REGISTER)
|
49
|
-
end
|
50
|
-
|
51
|
-
def teardown_server
|
52
|
-
if @print
|
53
|
-
p @server_calls, @client_calls
|
54
|
-
p @server_conv
|
55
|
-
p ERRORS[dde_get_last_error(@server_id)]
|
56
|
-
p ERRORS[dde_get_last_error(@client_id)]
|
57
|
-
@print = nil
|
58
|
-
end
|
59
|
-
dde_name_service(@server_id, @service_handle, DNS_UNREGISTER) if @server_id && @service_handle
|
60
|
-
dde_free_string_handle(@server_id, @service_handle) if @server_id && @service_handle
|
61
|
-
dde_free_string_handle(@server_id, @topic_handle) if @server_id && @topic_handle
|
62
|
-
dde_uninitialize(@server_id) if @server_id
|
63
|
-
dde_uninitialize(@client_id) if @client_id
|
64
|
-
if @conv_handle
|
65
|
-
dde_disconnect(@conv_handle)
|
66
|
-
@conv_handle = nil
|
67
|
-
end
|
68
|
-
@data = nil
|
69
|
-
end
|
70
|
-
|
71
|
-
describe Win::
|
72
|
-
|
73
|
-
describe '#register_clipboard_format' do
|
74
|
-
spec{ use{ RegisterClipboardFormat(format_name = "XlTable") }}
|
75
|
-
spec{ use{ register_clipboard_format(format_name = "XlTable") }}
|
76
|
-
|
77
|
-
it 'returns format id (int) if successfully registered format' do
|
78
|
-
id = register_clipboard_format("XlTable")
|
79
|
-
id.should_not == 0
|
80
|
-
id.should_not == nil
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'returns same format id for already registered format' do
|
84
|
-
id1 = register_clipboard_format("XlTable")
|
85
|
-
id2 = register_clipboard_format("XlTable")
|
86
|
-
id1.should == id2
|
87
|
-
end
|
88
|
-
|
89
|
-
it 'returns nil if not able to register format' do
|
90
|
-
register_clipboard_format("").should == nil
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
describe '#dde_initialize' do
|
95
|
-
after(:each) {dde_uninitialize(@id) if @id}
|
96
|
-
|
97
|
-
spec{ use{ status = DdeInitialize( id = zero_id, dde_callback, dde_cmd, unused=0); @id = id.read_long}}
|
98
|
-
spec{ use{ @id, status = dde_initialize(@id=0, dde_cmd, &dde_callback) }}
|
99
|
-
|
100
|
-
it 'with zero instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
|
101
|
-
@id, status = dde_initialize(0, APPCLASS_STANDARD) {|*args| }
|
102
|
-
@id.should be_an Integer
|
103
|
-
@id.should_not == 0
|
104
|
-
status.should == DMLERR_NO_ERROR
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'with nil instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
|
108
|
-
@id, status = dde_initialize(nil, APPCLASS_STANDARD) {|*args| }
|
109
|
-
@id.should be_an Integer
|
110
|
-
@id.should_not == 0
|
111
|
-
status.should == DMLERR_NO_ERROR
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'with omitted instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
|
115
|
-
@id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }
|
116
|
-
@id.should be_an Integer
|
117
|
-
@id.should_not == 0
|
118
|
-
status.should == DMLERR_NO_ERROR
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'returns error status if initialization unsuccessful' do
|
122
|
-
@id, status = dde_initialize(12345, APPCLASS_STANDARD) {|*args| }
|
123
|
-
status.should == DMLERR_INVALIDPARAMETER
|
124
|
-
@id.should == nil
|
125
|
-
end
|
126
|
-
|
127
|
-
it 'is able to reinitialize with correct id' do
|
128
|
-
@id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }
|
129
|
-
new_id, status = dde_initialize(@id, APPCLASS_STANDARD) {|*args| }
|
130
|
-
status.should == DMLERR_NO_ERROR
|
131
|
-
new_id.should == @id
|
132
|
-
end
|
133
|
-
end # describe 'dde_initialize'
|
134
|
-
|
135
|
-
context 'after initialization:' do
|
136
|
-
before(:each) {@id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }}
|
137
|
-
after(:each) {dde_uninitialize(@id) if @id}
|
138
|
-
|
139
|
-
describe '#dde_uninitialize' do
|
140
|
-
|
141
|
-
spec{ use{ status = DdeUninitialize( @id ) }}
|
142
|
-
spec{ use{ id, status = dde_uninitialize( @id) }}
|
143
|
-
|
144
|
-
it 'returns true if uninitialization successful' do
|
145
|
-
res = dde_uninitialize(@id)
|
146
|
-
res.should == true
|
147
|
-
end
|
148
|
-
|
149
|
-
it 'returns false if initialization unsuccessful' do
|
150
|
-
res = dde_uninitialize(12345)
|
151
|
-
res.should == false
|
152
|
-
end
|
153
|
-
end # describe '#dde_uninitialize'
|
154
|
-
|
155
|
-
describe '#dde_create_string_handle' do
|
156
|
-
after(:each) {dde_free_string_handle(@id, @string_handle) if @string_handle}
|
157
|
-
|
158
|
-
spec{ use{ @string_handle = DdeCreateStringHandle(id=0, string_pointer, code_page_id=CP_WINANSI) }}
|
159
|
-
spec{ use{ @string_handle = dde_create_string_handle(id=0, string='Any String', code_page_id=CP_WINANSI)}}
|
160
|
-
|
161
|
-
it 'returns nonzero Integer handle to a string (passable to other DDEML functions)' do
|
162
|
-
@string_handle = dde_create_string_handle(@id, 'My String', CP_WINANSI)
|
163
|
-
@string_handle.should be_an Integer
|
164
|
-
@string_handle.should_not == 0
|
165
|
-
end
|
166
|
-
|
167
|
-
it 'creates handle even if code_page is omitted' do
|
168
|
-
@string_handle = dde_create_string_handle(@id, 'My String')
|
169
|
-
@string_handle.should be_an Integer
|
170
|
-
@string_handle.should_not == 0
|
171
|
-
end
|
172
|
-
|
173
|
-
it 'creating two handles for the SAME string (inside one instance) USUALLY returns same handle' do
|
174
|
-
@string_handle = dde_create_string_handle(@id, 'My String')
|
175
|
-
10.times do
|
176
|
-
string_handle1 = dde_create_string_handle(@id, 'My String')
|
177
|
-
string_handle1.should == @string_handle
|
178
|
-
dde_free_string_handle(@id, string_handle1)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
it 'created different handles for two different strings ' do
|
183
|
-
@string_handle = dde_create_string_handle(@id, 'My String')
|
184
|
-
string_handle1 = dde_create_string_handle(@id, 'My String1')
|
185
|
-
string_handle1.should_not == @string_handle
|
186
|
-
dde_free_string_handle(@id, string_handle1)
|
187
|
-
end
|
188
|
-
|
189
|
-
it 'returns nil if unable to register handle to a string' do
|
190
|
-
@string_handle = dde_create_string_handle(@id, "", CP_WINANSI)
|
191
|
-
@string_handle.should == nil
|
192
|
-
end
|
193
|
-
end # describe '#dde_create_string_handle'
|
194
|
-
|
195
|
-
context "with dde string handle to 'My String 2'" do
|
196
|
-
before(:each) {@string_handle = dde_create_string_handle(@id, 'My String 2', CP_WINANSI)}
|
197
|
-
after(:each) {dde_free_string_handle(@id, @string_handle)}
|
198
|
-
|
199
|
-
describe '#dde_query_string' do
|
200
|
-
|
201
|
-
spec{ use{ string = DdeQueryString(@id, @string_handle, buffer, buffer.size, code_page=CP_WINANSI)}}
|
202
|
-
spec{ use{ string = dde_query_string(@id, @string_handle, code_page=CP_WINANSI )}}
|
203
|
-
|
204
|
-
it 'retrieves string that given string handle refers to' do
|
205
|
-
num_chars = DdeQueryString(@id, @string_handle, buf = buffer, buf.size, CP_WINANSI)
|
206
|
-
num_chars.should == 11
|
207
|
-
buf.read_string.should == 'My String 2'
|
208
|
-
end
|
209
|
-
|
210
|
-
it 'retrieves string that given string handle refers to' do
|
211
|
-
string = dde_query_string(@id, @string_handle, CP_WINANSI)
|
212
|
-
string.should == 'My String 2'
|
213
|
-
end
|
214
|
-
|
215
|
-
it 'retrieves string even if code_page is omitted' do
|
216
|
-
string = dde_query_string(@id, @string_handle)
|
217
|
-
string.should == 'My String 2'
|
218
|
-
end
|
219
|
-
|
220
|
-
it 'returns nil attempting to retrieve invalid handle' do
|
221
|
-
string = dde_query_string(@id, 12345)
|
222
|
-
string.should == nil
|
223
|
-
end
|
224
|
-
end # describe '#dde_query_string'
|
225
|
-
|
226
|
-
describe '#dde_free_string_handle' do
|
227
|
-
|
228
|
-
spec{ use{ success = DdeFreeStringHandle( @id, @string_handle)}}
|
229
|
-
spec{ use{ success = dde_free_string_handle( @id, @string_handle )}}
|
230
|
-
|
231
|
-
it 'returns true when freeing string handle registered with DDEML' do
|
232
|
-
res = dde_free_string_handle(@id, @string_handle)
|
233
|
-
res.should == true
|
234
|
-
end
|
235
|
-
|
236
|
-
it 'returns false attempting to free unregistered handle' do
|
237
|
-
res = dde_free_string_handle(@id, 12345)
|
238
|
-
res.should == false
|
239
|
-
end
|
240
|
-
|
241
|
-
it 'keeps string accessible while references to it still exist' do
|
242
|
-
# creates second handle to 'My String 2'
|
243
|
-
string_handle_1 = dde_create_string_handle(@id, 'My String 2', CP_WINANSI)
|
244
|
-
|
245
|
-
dde_free_string_handle(@id, @string_handle)
|
246
|
-
dde_query_string(@id, @string_handle).should == 'My String 2'
|
247
|
-
|
248
|
-
dde_free_string_handle(@id, string_handle_1)
|
249
|
-
dde_query_string(@id, @string_handle).should == nil
|
250
|
-
end
|
251
|
-
|
252
|
-
it 'makes string inaccessible once its last handle is freed' do
|
253
|
-
dde_free_string_handle(@id, @string_handle)
|
254
|
-
dde_query_string(@id, @string_handle).should == nil
|
255
|
-
end
|
256
|
-
end # describe '#dde_free_string_handle'
|
257
|
-
|
258
|
-
describe '#dde_get_last_error' do
|
259
|
-
spec{ use{ error_code = DdeGetLastError(@id) }}
|
260
|
-
spec{ use{ error_code = dde_get_last_error(@id) }}
|
261
|
-
|
262
|
-
it 'original API returns DMLERR_NO_ERROR if there is no last DDE error for given app instance' do
|
263
|
-
DdeGetLastError(@id).should == DMLERR_NO_ERROR
|
264
|
-
end
|
265
|
-
|
266
|
-
it 'snake_case API returns nil if there is no last DDE error for given app instance' do
|
267
|
-
dde_get_last_error(@id).should == nil
|
268
|
-
end
|
269
|
-
|
270
|
-
it 'returns error code of last DDE error for given app instance' do
|
271
|
-
dde_name_service(@id, 1234, DNS_REGISTER )
|
272
|
-
dde_get_last_error(@id).should == DMLERR_INVALIDPARAMETER
|
273
|
-
end
|
274
|
-
end # describe '#dde_get_last_error'
|
275
|
-
|
276
|
-
end # context "with dde string handle to 'My String'"
|
277
|
-
end # context 'after initialization:'
|
278
|
-
|
279
|
-
context 'with synthetic DDE client/server' do
|
280
|
-
before(:each){ setup_server }
|
281
|
-
after(:each) { teardown_server}
|
282
|
-
|
283
|
-
describe '#dde_name_service' do
|
284
|
-
spec{ use{ success = dde_name_service(@server_id, @service_handle, cmd=DNS_UNREGISTER ) }}
|
285
|
-
spec{ use{ success = DdeNameService(@server_id, @service_handle, reserved=0, cmd=DNS_UNREGISTER) }}
|
286
|
-
|
287
|
-
it 'registers or unregisters the service names that DDE server supports' do
|
288
|
-
pending 'Register/Unregister messages don`t show up in @server_calls :('
|
289
|
-
success = dde_name_service(@server_id, @service_handle, DNS_REGISTER )
|
290
|
-
success.should == true
|
291
|
-
success = dde_name_service(@server_id, @service_handle, DNS_UNREGISTER )
|
292
|
-
success.should == true
|
293
|
-
end
|
294
|
-
end # describe '#dde_name_service'
|
295
|
-
|
296
|
-
describe '#dde_connect' do
|
297
|
-
spec{ use{ @conv_handle = DdeConnect( instance_id=0, service=0, topic=0, context=nil) }}
|
298
|
-
spec{ use{ @conv_handle = dde_connect( instance_id=0, service=0, topic=0, context=nil) }}
|
299
|
-
|
300
|
-
it 'connects to existing DDE server (self in this case)' do
|
301
|
-
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
302
|
-
|
303
|
-
@server_calls.first[0].should == 'XTYP_CONNECT'
|
304
|
-
@server_calls.first[3].should == 'topic 2'
|
305
|
-
@server_calls.first[4].should == 'service 2'
|
306
|
-
@server_calls[1][0].should == 'XTYP_CONNECT_CONFIRM'
|
307
|
-
@server_calls[1][3].should == 'topic 2'
|
308
|
-
@server_calls[1][4].should == 'service 2'
|
309
|
-
dde_disconnect(@server_conv).should == true
|
310
|
-
dde_disconnect(@conv_handle).should == true
|
311
|
-
end
|
312
|
-
|
313
|
-
it 'connects to existing DDE server (from @client, NOT self)' do
|
314
|
-
pending 'something is wrong when connecting to separate service instance, uninitialize fails'
|
315
|
-
@conv_handle = dde_connect( @client_id, @service_handle, @topic_handle, context=nil)
|
316
|
-
puts @conv_handle
|
317
|
-
end
|
318
|
-
end # describe '#dde_connect'
|
319
|
-
|
320
|
-
describe '#dde_disconnect' do
|
321
|
-
spec{ use{ success = DdeDisconnect(conversation_handle=0) }}
|
322
|
-
spec{ use{ success = dde_disconnect(conversation_handle=0) }}
|
323
|
-
|
324
|
-
it 'fails to disconnect if not valid conversation handle given' do
|
325
|
-
dde_disconnect(12345).should == false
|
326
|
-
end
|
327
|
-
|
328
|
-
it 'disconnects from existing (self) DDE server' do
|
329
|
-
conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
330
|
-
dde_disconnect(conv_handle).should == true
|
331
|
-
end
|
332
|
-
|
333
|
-
it 'disconnects from existing (self) DDE server' do
|
334
|
-
pending 'XTYP_DISCONNECT is not received by server callback (since we are disconnecting from self?)'
|
335
|
-
conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
336
|
-
dde_disconnect(conv_handle).should == true
|
337
|
-
@server_calls.last[0].should == "XTYP_DISCONNECT"
|
338
|
-
p @server_calls, @client_calls, @server_conv
|
339
|
-
p ERRORS[dde_get_last_error(@server_id)]
|
340
|
-
p ERRORS[dde_get_last_error(@client_id)]
|
341
|
-
end
|
342
|
-
end # describe '#dde_disconnect'
|
343
|
-
end # context 'with synthetic DDE server'
|
344
|
-
|
345
|
-
describe "#dde_client_transaction" do
|
346
|
-
before(:each) do
|
347
|
-
setup_server do |*args|
|
348
|
-
@server_calls << extract_values(*args)
|
349
|
-
@data_out, size = dde_get_data(args[5]) if args.first == XTYP_POKE || args.first == XTYP_EXECUTE
|
350
|
-
DDE_FACK
|
351
|
-
end
|
352
|
-
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
353
|
-
@data_in = FFI::MemoryPointer.from_string POKE_STRING
|
354
|
-
end
|
355
|
-
after(:each) { teardown_server}
|
356
|
-
|
357
|
-
spec{ use{ DdeClientTransaction(data=nil, size=0, conv=0, item=0, format=0, type=0, timeout=0, result=nil) }}
|
358
|
-
spec{ use{ dde_client_transaction(data=nil, size=0, conv=0, item=0, format=0, type=0, timeout=0, result=nil) }}
|
359
|
-
|
360
|
-
|
361
|
-
it "returns 0/nil if initiated transaction unsuccessful" do
|
362
|
-
res = DdeClientTransaction(@data_in, @data_in.size, 1234, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
363
|
-
res.should == 0 # wrong conversation handle
|
364
|
-
res = dde_client_transaction(@data_in, @data_in.size, 1234, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
365
|
-
res.should == nil # wrong conversation handle
|
366
|
-
res = dde_client_transaction(@data_in, @data_in.size, @conv_handle, 0, CF_TEXT, XTYP_POKE, 1000, nil)
|
367
|
-
res.should == nil # wrong item handle (cannot be NULL in XTYP_POKE transaction)
|
368
|
-
@server_calls.any? {|call| call[0] == 'XTYP_POKE'}.should be_false
|
369
|
-
end
|
370
|
-
|
371
|
-
it "original api is used by CLIENT to begins a data transaction with server" do
|
372
|
-
res = DdeClientTransaction(@data_in, @data_in.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
373
|
-
res.should == 1
|
374
|
-
@server_calls.any? {|call| call[0] == 'XTYP_POKE'}.should be_true
|
375
|
-
@data_out.read_string.should == POKE_STRING.rstrip
|
376
|
-
end
|
377
|
-
|
378
|
-
it "snake_case api begins a data transaction between a client and a server" do
|
379
|
-
res = dde_client_transaction(@data_in, @data_in.size, @conv_handle, 0, 0, XTYP_EXECUTE, 1000, nil)
|
380
|
-
res.should be_true
|
381
|
-
@server_calls.any? {|call| call[0] == 'XTYP_EXECUTE'}.should be_true
|
382
|
-
@data_out.read_string.should == POKE_STRING.rstrip
|
383
|
-
end
|
384
|
-
end # describe dde_client_transaction
|
385
|
-
|
386
|
-
describe '#dde_get_data' do
|
387
|
-
after(:each) { teardown_server}
|
388
|
-
|
389
|
-
spec{ use{ data_pointer, size = dde_get_data( data_handle = 123, max = 1073741823, offset = 0) }}
|
390
|
-
spec{ use{ size = DdeGetData( data_handle = 123, nil, 0, 0) }} # returns dde data set size in bytes
|
391
|
-
spec{ use{ size = DdeGetData( data_handle = 123, FFI::MemoryPointer.new(:char, 1024), max = 1024, offset = 0) }}
|
392
|
-
|
393
|
-
it 'original API returns 0 if trying to address invalid dde data handle' do
|
394
|
-
DdeGetData( data_handle = 123, nil, 0, 0).should == 0
|
395
|
-
end
|
396
|
-
|
397
|
-
it 'snake_case API returns [nil, 0] if trying to address invalid dde data handle' do
|
398
|
-
data, size = dde_get_data( data_handle = 123, 3741823, 0)
|
399
|
-
data.should == nil
|
400
|
-
end
|
401
|
-
|
402
|
-
it 'returns dde data if used inside dde callback block' do
|
403
|
-
setup_server do |*args|
|
404
|
-
@server_calls << extract_values(*args);
|
405
|
-
if args[0] == XTYP_POKE
|
406
|
-
data_handle = args[5]
|
407
|
-
data, size = dde_get_data(data_handle)
|
408
|
-
data.should be_an FFI::MemoryPointer
|
409
|
-
data.read_string.should == POKE_STRING.rstrip
|
410
|
-
size.should == 12
|
411
|
-
DdeGetData(data_handle, nil, 0, 0).should == 12
|
412
|
-
data = FFI::MemoryPointer.new(:char, 1024)
|
413
|
-
DdeGetData(data_handle, data, data.size, 0).should == 12
|
414
|
-
data.read_string.should == POKE_STRING.rstrip
|
415
|
-
end
|
416
|
-
DDE_FACK
|
417
|
-
end
|
418
|
-
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
419
|
-
str = FFI::MemoryPointer.from_string POKE_STRING
|
420
|
-
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
421
|
-
end
|
422
|
-
|
423
|
-
it 'dde data handle expires once transaction is finished (DDE_FACK)' do
|
424
|
-
setup_server do |*args|
|
425
|
-
@server_calls << extract_values(*args);
|
426
|
-
DDE_FACK
|
427
|
-
end
|
428
|
-
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
429
|
-
str = FFI::MemoryPointer.from_string POKE_STRING
|
430
|
-
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
431
|
-
|
432
|
-
# only inside callback block dde data handle is valid (while transaction still in progress)
|
433
|
-
data, size = dde_get_data(@server_calls.last[5])
|
434
|
-
data.should == nil
|
435
|
-
size.should == 0
|
436
|
-
end
|
437
|
-
|
438
|
-
end # describe '#dde_get_data'
|
439
|
-
|
440
|
-
describe "#dde_access_data" do
|
441
|
-
after(:each) { teardown_server}
|
442
|
-
|
443
|
-
spec{ use{ success = DdeAccessData(data_handle = 123, data_size=zero_id) }}
|
444
|
-
spec{ use{ data_pointer, size = dde_access_data(data_handle = 123) }}
|
445
|
-
|
446
|
-
it "provides access to the data in the specified DDE data handle (both inside and outside of callback)" do
|
447
|
-
setup_server do |*args|
|
448
|
-
@server_calls << extract_values(*args)
|
449
|
-
if args[0] == XTYP_POKE
|
450
|
-
data_handle = args[5]
|
451
|
-
data, size = dde_access_data(data_handle)
|
452
|
-
data.should be_kind_of FFI::Pointer
|
453
|
-
data.read_string.should == POKE_STRING.rstrip
|
454
|
-
size.should == 12
|
455
|
-
buf = FFI::MemoryPointer.new(:int16)
|
456
|
-
data = DdeAccessData(data_handle, buf)
|
457
|
-
buf.get_int16(0).should == 12
|
458
|
-
data.should be_kind_of FFI::Pointer
|
459
|
-
data.read_string.should == POKE_STRING.rstrip
|
460
|
-
dde_unaccess_data(data_handle)
|
461
|
-
end
|
462
|
-
DDE_FACK
|
463
|
-
end
|
464
|
-
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
465
|
-
str = FFI::MemoryPointer.from_string POKE_STRING
|
466
|
-
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
467
|
-
end
|
468
|
-
|
469
|
-
it 'dde data handle expires once transaction is finished (DDE_FACK)' do
|
470
|
-
setup_server do |*args|
|
471
|
-
@server_calls << extract_values(*args);
|
472
|
-
DDE_FACK
|
473
|
-
end
|
474
|
-
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
475
|
-
str = FFI::MemoryPointer.from_string POKE_STRING
|
476
|
-
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
477
|
-
|
478
|
-
# only inside callback block dde data handle is valid (while transaction still in progress)
|
479
|
-
data, size = dde_access_data(@server_calls.last[5])
|
480
|
-
data.should == nil
|
481
|
-
size.should == 0
|
482
|
-
end
|
483
|
-
end # describe dde_access_data
|
484
|
-
|
485
|
-
describe "#dde_unaccess_data" do
|
486
|
-
spec{ use{ success = DdeUnaccessData(data_handle = 123) }}
|
487
|
-
spec{ use{ success = dde_unaccess_data(data_handle = 123) }}
|
488
|
-
|
489
|
-
it "returns 0/false if given invalid DDE data handle " do
|
490
|
-
DdeUnaccessData(data_handle = 123).should == 0
|
491
|
-
dde_unaccess_data(data_handle = 123).should == false
|
492
|
-
end
|
493
|
-
|
494
|
-
it "unaccesses a DDE data handle that was previously accessed" do
|
495
|
-
setup_server do |*args|
|
496
|
-
@server_calls << extract_values(*args);
|
497
|
-
if args[0] == XTYP_POKE
|
498
|
-
data_handle = args[5]
|
499
|
-
dde_unaccess_data(data_handle).should == true
|
500
|
-
|
501
|
-
data, size = dde_access_data(data_handle)
|
502
|
-
data.should be_kind_of FFI::Pointer
|
503
|
-
data.read_string.should == POKE_STRING.rstrip
|
504
|
-
|
505
|
-
dde_unaccess_data(data_handle).should == true
|
506
|
-
end
|
507
|
-
DDE_FACK
|
508
|
-
end
|
509
|
-
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
510
|
-
str = FFI::MemoryPointer.from_string POKE_STRING
|
511
|
-
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
512
|
-
end
|
513
|
-
|
514
|
-
it 'dde data handle expires once transaction is finished (DDE_FACK)' do
|
515
|
-
setup_server do |*args|
|
516
|
-
@server_calls << extract_values(*args);
|
517
|
-
DDE_FACK
|
518
|
-
end
|
519
|
-
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
520
|
-
str = FFI::MemoryPointer.from_string POKE_STRING
|
521
|
-
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
522
|
-
|
523
|
-
# only inside callback block dde data handle is valid (while transaction still in progress)
|
524
|
-
dde_unaccess_data(@server_calls.last[5]).should == false
|
525
|
-
end
|
526
|
-
|
527
|
-
end # describe dde_unaccess_data
|
528
|
-
end
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'win/dde'
|
3
|
+
|
4
|
+
module WinDdeTest
|
5
|
+
include WinTest
|
6
|
+
include Win::Dde
|
7
|
+
include Win::Gui::Message
|
8
|
+
|
9
|
+
POKE_STRING = "Poke_string"
|
10
|
+
|
11
|
+
def dde_cmd
|
12
|
+
APPCLASS_STANDARD
|
13
|
+
end
|
14
|
+
|
15
|
+
def dde_callback
|
16
|
+
->(*args){}
|
17
|
+
end
|
18
|
+
|
19
|
+
def zero_id
|
20
|
+
FFI::MemoryPointer.new(:long).write_long(0)
|
21
|
+
end
|
22
|
+
|
23
|
+
def buffer
|
24
|
+
FFI::MemoryPointer.new(:char, 1024)
|
25
|
+
end
|
26
|
+
|
27
|
+
def string_pointer
|
28
|
+
FFI::MemoryPointer.from_string("Pointer_string")
|
29
|
+
end
|
30
|
+
|
31
|
+
def extract_values(*args)
|
32
|
+
type, format, conv, hsz1, hsz2, data, data1, data2 = *args
|
33
|
+
@server_conv = conv
|
34
|
+
[Win::Dde::TYPES[type], format, conv,
|
35
|
+
dde_query_string(@client_id, hsz1),
|
36
|
+
dde_query_string(@client_id, hsz2),
|
37
|
+
data, data1, data2]
|
38
|
+
end
|
39
|
+
|
40
|
+
def setup_server(&server_block)
|
41
|
+
@client_calls = []
|
42
|
+
@server_calls = []
|
43
|
+
@client_id, st = dde_initialize(APPCLASS_STANDARD) {|*args| @client_calls << extract_values(*args); DDE_FACK}
|
44
|
+
@server_id, st = dde_initialize(APPCLASS_STANDARD,
|
45
|
+
&server_block || proc {|*args| @server_calls << extract_values(*args); DDE_FACK} )
|
46
|
+
@service_handle = dde_create_string_handle(@server_id, 'service 2', CP_WINANSI)
|
47
|
+
@topic_handle = dde_create_string_handle(@server_id, 'topic 2', CP_WINANSI)
|
48
|
+
dde_name_service(@server_id, @service_handle, DNS_REGISTER)
|
49
|
+
end
|
50
|
+
|
51
|
+
def teardown_server
|
52
|
+
if @print
|
53
|
+
p @server_calls, @client_calls
|
54
|
+
p @server_conv
|
55
|
+
p ERRORS[dde_get_last_error(@server_id)]
|
56
|
+
p ERRORS[dde_get_last_error(@client_id)]
|
57
|
+
@print = nil
|
58
|
+
end
|
59
|
+
dde_name_service(@server_id, @service_handle, DNS_UNREGISTER) if @server_id && @service_handle
|
60
|
+
dde_free_string_handle(@server_id, @service_handle) if @server_id && @service_handle
|
61
|
+
dde_free_string_handle(@server_id, @topic_handle) if @server_id && @topic_handle
|
62
|
+
dde_uninitialize(@server_id) if @server_id
|
63
|
+
dde_uninitialize(@client_id) if @client_id
|
64
|
+
if @conv_handle
|
65
|
+
dde_disconnect(@conv_handle)
|
66
|
+
@conv_handle = nil
|
67
|
+
end
|
68
|
+
@data = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
describe Win::Dde, ' contains a set of pre-defined Windows API functions' do
|
72
|
+
|
73
|
+
describe '#register_clipboard_format' do
|
74
|
+
spec{ use{ RegisterClipboardFormat(format_name = "XlTable") }}
|
75
|
+
spec{ use{ register_clipboard_format(format_name = "XlTable") }}
|
76
|
+
|
77
|
+
it 'returns format id (int) if successfully registered format' do
|
78
|
+
id = register_clipboard_format("XlTable")
|
79
|
+
id.should_not == 0
|
80
|
+
id.should_not == nil
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'returns same format id for already registered format' do
|
84
|
+
id1 = register_clipboard_format("XlTable")
|
85
|
+
id2 = register_clipboard_format("XlTable")
|
86
|
+
id1.should == id2
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'returns nil if not able to register format' do
|
90
|
+
register_clipboard_format("").should == nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#dde_initialize' do
|
95
|
+
after(:each) {dde_uninitialize(@id) if @id}
|
96
|
+
|
97
|
+
spec{ use{ status = DdeInitialize( id = zero_id, dde_callback, dde_cmd, unused=0); @id = id.read_long}}
|
98
|
+
spec{ use{ @id, status = dde_initialize(@id=0, dde_cmd, &dde_callback) }}
|
99
|
+
|
100
|
+
it 'with zero instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
|
101
|
+
@id, status = dde_initialize(0, APPCLASS_STANDARD) {|*args| }
|
102
|
+
@id.should be_an Integer
|
103
|
+
@id.should_not == 0
|
104
|
+
status.should == DMLERR_NO_ERROR
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'with nil instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
|
108
|
+
@id, status = dde_initialize(nil, APPCLASS_STANDARD) {|*args| }
|
109
|
+
@id.should be_an Integer
|
110
|
+
@id.should_not == 0
|
111
|
+
status.should == DMLERR_NO_ERROR
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'with omitted instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
|
115
|
+
@id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }
|
116
|
+
@id.should be_an Integer
|
117
|
+
@id.should_not == 0
|
118
|
+
status.should == DMLERR_NO_ERROR
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'returns error status if initialization unsuccessful' do
|
122
|
+
@id, status = dde_initialize(12345, APPCLASS_STANDARD) {|*args| }
|
123
|
+
status.should == DMLERR_INVALIDPARAMETER
|
124
|
+
@id.should == nil
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'is able to reinitialize with correct id' do
|
128
|
+
@id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }
|
129
|
+
new_id, status = dde_initialize(@id, APPCLASS_STANDARD) {|*args| }
|
130
|
+
status.should == DMLERR_NO_ERROR
|
131
|
+
new_id.should == @id
|
132
|
+
end
|
133
|
+
end # describe 'dde_initialize'
|
134
|
+
|
135
|
+
context 'after initialization:' do
|
136
|
+
before(:each) {@id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }}
|
137
|
+
after(:each) {dde_uninitialize(@id) if @id}
|
138
|
+
|
139
|
+
describe '#dde_uninitialize' do
|
140
|
+
|
141
|
+
spec{ use{ status = DdeUninitialize( @id ) }}
|
142
|
+
spec{ use{ id, status = dde_uninitialize( @id) }}
|
143
|
+
|
144
|
+
it 'returns true if uninitialization successful' do
|
145
|
+
res = dde_uninitialize(@id)
|
146
|
+
res.should == true
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'returns false if initialization unsuccessful' do
|
150
|
+
res = dde_uninitialize(12345)
|
151
|
+
res.should == false
|
152
|
+
end
|
153
|
+
end # describe '#dde_uninitialize'
|
154
|
+
|
155
|
+
describe '#dde_create_string_handle' do
|
156
|
+
after(:each) {dde_free_string_handle(@id, @string_handle) if @string_handle}
|
157
|
+
|
158
|
+
spec{ use{ @string_handle = DdeCreateStringHandle(id=0, string_pointer, code_page_id=CP_WINANSI) }}
|
159
|
+
spec{ use{ @string_handle = dde_create_string_handle(id=0, string='Any String', code_page_id=CP_WINANSI)}}
|
160
|
+
|
161
|
+
it 'returns nonzero Integer handle to a string (passable to other DDEML functions)' do
|
162
|
+
@string_handle = dde_create_string_handle(@id, 'My String', CP_WINANSI)
|
163
|
+
@string_handle.should be_an Integer
|
164
|
+
@string_handle.should_not == 0
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'creates handle even if code_page is omitted' do
|
168
|
+
@string_handle = dde_create_string_handle(@id, 'My String')
|
169
|
+
@string_handle.should be_an Integer
|
170
|
+
@string_handle.should_not == 0
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'creating two handles for the SAME string (inside one instance) USUALLY returns same handle' do
|
174
|
+
@string_handle = dde_create_string_handle(@id, 'My String')
|
175
|
+
10.times do
|
176
|
+
string_handle1 = dde_create_string_handle(@id, 'My String')
|
177
|
+
string_handle1.should == @string_handle
|
178
|
+
dde_free_string_handle(@id, string_handle1)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'created different handles for two different strings ' do
|
183
|
+
@string_handle = dde_create_string_handle(@id, 'My String')
|
184
|
+
string_handle1 = dde_create_string_handle(@id, 'My String1')
|
185
|
+
string_handle1.should_not == @string_handle
|
186
|
+
dde_free_string_handle(@id, string_handle1)
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'returns nil if unable to register handle to a string' do
|
190
|
+
@string_handle = dde_create_string_handle(@id, "", CP_WINANSI)
|
191
|
+
@string_handle.should == nil
|
192
|
+
end
|
193
|
+
end # describe '#dde_create_string_handle'
|
194
|
+
|
195
|
+
context "with dde string handle to 'My String 2'" do
|
196
|
+
before(:each) {@string_handle = dde_create_string_handle(@id, 'My String 2', CP_WINANSI)}
|
197
|
+
after(:each) {dde_free_string_handle(@id, @string_handle)}
|
198
|
+
|
199
|
+
describe '#dde_query_string' do
|
200
|
+
|
201
|
+
spec{ use{ string = DdeQueryString(@id, @string_handle, buffer, buffer.size, code_page=CP_WINANSI)}}
|
202
|
+
spec{ use{ string = dde_query_string(@id, @string_handle, code_page=CP_WINANSI )}}
|
203
|
+
|
204
|
+
it 'retrieves string that given string handle refers to' do
|
205
|
+
num_chars = DdeQueryString(@id, @string_handle, buf = buffer, buf.size, CP_WINANSI)
|
206
|
+
num_chars.should == 11
|
207
|
+
buf.read_string.should == 'My String 2'
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'retrieves string that given string handle refers to' do
|
211
|
+
string = dde_query_string(@id, @string_handle, CP_WINANSI)
|
212
|
+
string.should == 'My String 2'
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'retrieves string even if code_page is omitted' do
|
216
|
+
string = dde_query_string(@id, @string_handle)
|
217
|
+
string.should == 'My String 2'
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'returns nil attempting to retrieve invalid handle' do
|
221
|
+
string = dde_query_string(@id, 12345)
|
222
|
+
string.should == nil
|
223
|
+
end
|
224
|
+
end # describe '#dde_query_string'
|
225
|
+
|
226
|
+
describe '#dde_free_string_handle' do
|
227
|
+
|
228
|
+
spec{ use{ success = DdeFreeStringHandle( @id, @string_handle)}}
|
229
|
+
spec{ use{ success = dde_free_string_handle( @id, @string_handle )}}
|
230
|
+
|
231
|
+
it 'returns true when freeing string handle registered with DDEML' do
|
232
|
+
res = dde_free_string_handle(@id, @string_handle)
|
233
|
+
res.should == true
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'returns false attempting to free unregistered handle' do
|
237
|
+
res = dde_free_string_handle(@id, 12345)
|
238
|
+
res.should == false
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'keeps string accessible while references to it still exist' do
|
242
|
+
# creates second handle to 'My String 2'
|
243
|
+
string_handle_1 = dde_create_string_handle(@id, 'My String 2', CP_WINANSI)
|
244
|
+
|
245
|
+
dde_free_string_handle(@id, @string_handle)
|
246
|
+
dde_query_string(@id, @string_handle).should == 'My String 2'
|
247
|
+
|
248
|
+
dde_free_string_handle(@id, string_handle_1)
|
249
|
+
dde_query_string(@id, @string_handle).should == nil
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'makes string inaccessible once its last handle is freed' do
|
253
|
+
dde_free_string_handle(@id, @string_handle)
|
254
|
+
dde_query_string(@id, @string_handle).should == nil
|
255
|
+
end
|
256
|
+
end # describe '#dde_free_string_handle'
|
257
|
+
|
258
|
+
describe '#dde_get_last_error' do
|
259
|
+
spec{ use{ error_code = DdeGetLastError(@id) }}
|
260
|
+
spec{ use{ error_code = dde_get_last_error(@id) }}
|
261
|
+
|
262
|
+
it 'original API returns DMLERR_NO_ERROR if there is no last DDE error for given app instance' do
|
263
|
+
DdeGetLastError(@id).should == DMLERR_NO_ERROR
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'snake_case API returns nil if there is no last DDE error for given app instance' do
|
267
|
+
dde_get_last_error(@id).should == nil
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'returns error code of last DDE error for given app instance' do
|
271
|
+
dde_name_service(@id, 1234, DNS_REGISTER )
|
272
|
+
dde_get_last_error(@id).should == DMLERR_INVALIDPARAMETER
|
273
|
+
end
|
274
|
+
end # describe '#dde_get_last_error'
|
275
|
+
|
276
|
+
end # context "with dde string handle to 'My String'"
|
277
|
+
end # context 'after initialization:'
|
278
|
+
|
279
|
+
context 'with synthetic DDE client/server' do
|
280
|
+
before(:each){ setup_server }
|
281
|
+
after(:each) { teardown_server}
|
282
|
+
|
283
|
+
describe '#dde_name_service' do
|
284
|
+
spec{ use{ success = dde_name_service(@server_id, @service_handle, cmd=DNS_UNREGISTER ) }}
|
285
|
+
spec{ use{ success = DdeNameService(@server_id, @service_handle, reserved=0, cmd=DNS_UNREGISTER) }}
|
286
|
+
|
287
|
+
it 'registers or unregisters the service names that DDE server supports' do
|
288
|
+
pending 'Register/Unregister messages don`t show up in @server_calls :('
|
289
|
+
success = dde_name_service(@server_id, @service_handle, DNS_REGISTER )
|
290
|
+
success.should == true
|
291
|
+
success = dde_name_service(@server_id, @service_handle, DNS_UNREGISTER )
|
292
|
+
success.should == true
|
293
|
+
end
|
294
|
+
end # describe '#dde_name_service'
|
295
|
+
|
296
|
+
describe '#dde_connect' do
|
297
|
+
spec{ use{ @conv_handle = DdeConnect( instance_id=0, service=0, topic=0, context=nil) }}
|
298
|
+
spec{ use{ @conv_handle = dde_connect( instance_id=0, service=0, topic=0, context=nil) }}
|
299
|
+
|
300
|
+
it 'connects to existing DDE server (self in this case)' do
|
301
|
+
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
302
|
+
|
303
|
+
@server_calls.first[0].should == 'XTYP_CONNECT'
|
304
|
+
@server_calls.first[3].should == 'topic 2'
|
305
|
+
@server_calls.first[4].should == 'service 2'
|
306
|
+
@server_calls[1][0].should == 'XTYP_CONNECT_CONFIRM'
|
307
|
+
@server_calls[1][3].should == 'topic 2'
|
308
|
+
@server_calls[1][4].should == 'service 2'
|
309
|
+
dde_disconnect(@server_conv).should == true
|
310
|
+
dde_disconnect(@conv_handle).should == true
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'connects to existing DDE server (from @client, NOT self)' do
|
314
|
+
pending 'something is wrong when connecting to separate service instance, uninitialize fails'
|
315
|
+
@conv_handle = dde_connect( @client_id, @service_handle, @topic_handle, context=nil)
|
316
|
+
puts @conv_handle
|
317
|
+
end
|
318
|
+
end # describe '#dde_connect'
|
319
|
+
|
320
|
+
describe '#dde_disconnect' do
|
321
|
+
spec{ use{ success = DdeDisconnect(conversation_handle=0) }}
|
322
|
+
spec{ use{ success = dde_disconnect(conversation_handle=0) }}
|
323
|
+
|
324
|
+
it 'fails to disconnect if not valid conversation handle given' do
|
325
|
+
dde_disconnect(12345).should == false
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'disconnects from existing (self) DDE server' do
|
329
|
+
conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
330
|
+
dde_disconnect(conv_handle).should == true
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'disconnects from existing (self) DDE server' do
|
334
|
+
pending 'XTYP_DISCONNECT is not received by server callback (since we are disconnecting from self?)'
|
335
|
+
conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
336
|
+
dde_disconnect(conv_handle).should == true
|
337
|
+
@server_calls.last[0].should == "XTYP_DISCONNECT"
|
338
|
+
p @server_calls, @client_calls, @server_conv
|
339
|
+
p ERRORS[dde_get_last_error(@server_id)]
|
340
|
+
p ERRORS[dde_get_last_error(@client_id)]
|
341
|
+
end
|
342
|
+
end # describe '#dde_disconnect'
|
343
|
+
end # context 'with synthetic DDE server'
|
344
|
+
|
345
|
+
describe "#dde_client_transaction" do
|
346
|
+
before(:each) do
|
347
|
+
setup_server do |*args|
|
348
|
+
@server_calls << extract_values(*args)
|
349
|
+
@data_out, size = dde_get_data(args[5]) if args.first == XTYP_POKE || args.first == XTYP_EXECUTE
|
350
|
+
DDE_FACK
|
351
|
+
end
|
352
|
+
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
353
|
+
@data_in = FFI::MemoryPointer.from_string POKE_STRING
|
354
|
+
end
|
355
|
+
after(:each) { teardown_server}
|
356
|
+
|
357
|
+
spec{ use{ DdeClientTransaction(data=nil, size=0, conv=0, item=0, format=0, type=0, timeout=0, result=nil) }}
|
358
|
+
spec{ use{ dde_client_transaction(data=nil, size=0, conv=0, item=0, format=0, type=0, timeout=0, result=nil) }}
|
359
|
+
|
360
|
+
|
361
|
+
it "returns 0/nil if initiated transaction unsuccessful" do
|
362
|
+
res = DdeClientTransaction(@data_in, @data_in.size, 1234, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
363
|
+
res.should == 0 # wrong conversation handle
|
364
|
+
res = dde_client_transaction(@data_in, @data_in.size, 1234, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
365
|
+
res.should == nil # wrong conversation handle
|
366
|
+
res = dde_client_transaction(@data_in, @data_in.size, @conv_handle, 0, CF_TEXT, XTYP_POKE, 1000, nil)
|
367
|
+
res.should == nil # wrong item handle (cannot be NULL in XTYP_POKE transaction)
|
368
|
+
@server_calls.any? {|call| call[0] == 'XTYP_POKE'}.should be_false
|
369
|
+
end
|
370
|
+
|
371
|
+
it "original api is used by CLIENT to begins a data transaction with server" do
|
372
|
+
res = DdeClientTransaction(@data_in, @data_in.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
373
|
+
res.should == 1
|
374
|
+
@server_calls.any? {|call| call[0] == 'XTYP_POKE'}.should be_true
|
375
|
+
@data_out.read_string.should == POKE_STRING.rstrip
|
376
|
+
end
|
377
|
+
|
378
|
+
it "snake_case api begins a data transaction between a client and a server" do
|
379
|
+
res = dde_client_transaction(@data_in, @data_in.size, @conv_handle, 0, 0, XTYP_EXECUTE, 1000, nil)
|
380
|
+
res.should be_true
|
381
|
+
@server_calls.any? {|call| call[0] == 'XTYP_EXECUTE'}.should be_true
|
382
|
+
@data_out.read_string.should == POKE_STRING.rstrip
|
383
|
+
end
|
384
|
+
end # describe dde_client_transaction
|
385
|
+
|
386
|
+
describe '#dde_get_data' do
|
387
|
+
after(:each) { teardown_server}
|
388
|
+
|
389
|
+
spec{ use{ data_pointer, size = dde_get_data( data_handle = 123, max = 1073741823, offset = 0) }}
|
390
|
+
spec{ use{ size = DdeGetData( data_handle = 123, nil, 0, 0) }} # returns dde data set size in bytes
|
391
|
+
spec{ use{ size = DdeGetData( data_handle = 123, FFI::MemoryPointer.new(:char, 1024), max = 1024, offset = 0) }}
|
392
|
+
|
393
|
+
it 'original API returns 0 if trying to address invalid dde data handle' do
|
394
|
+
DdeGetData( data_handle = 123, nil, 0, 0).should == 0
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'snake_case API returns [nil, 0] if trying to address invalid dde data handle' do
|
398
|
+
data, size = dde_get_data( data_handle = 123, 3741823, 0)
|
399
|
+
data.should == nil
|
400
|
+
end
|
401
|
+
|
402
|
+
it 'returns dde data if used inside dde callback block' do
|
403
|
+
setup_server do |*args|
|
404
|
+
@server_calls << extract_values(*args);
|
405
|
+
if args[0] == XTYP_POKE
|
406
|
+
data_handle = args[5]
|
407
|
+
data, size = dde_get_data(data_handle)
|
408
|
+
data.should be_an FFI::MemoryPointer
|
409
|
+
data.read_string.should == POKE_STRING.rstrip
|
410
|
+
size.should == 12
|
411
|
+
DdeGetData(data_handle, nil, 0, 0).should == 12
|
412
|
+
data = FFI::MemoryPointer.new(:char, 1024)
|
413
|
+
DdeGetData(data_handle, data, data.size, 0).should == 12
|
414
|
+
data.read_string.should == POKE_STRING.rstrip
|
415
|
+
end
|
416
|
+
DDE_FACK
|
417
|
+
end
|
418
|
+
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
419
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
420
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
421
|
+
end
|
422
|
+
|
423
|
+
it 'dde data handle expires once transaction is finished (DDE_FACK)' do
|
424
|
+
setup_server do |*args|
|
425
|
+
@server_calls << extract_values(*args);
|
426
|
+
DDE_FACK
|
427
|
+
end
|
428
|
+
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
429
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
430
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
431
|
+
|
432
|
+
# only inside callback block dde data handle is valid (while transaction still in progress)
|
433
|
+
data, size = dde_get_data(@server_calls.last[5])
|
434
|
+
data.should == nil
|
435
|
+
size.should == 0
|
436
|
+
end
|
437
|
+
|
438
|
+
end # describe '#dde_get_data'
|
439
|
+
|
440
|
+
describe "#dde_access_data" do
|
441
|
+
after(:each) { teardown_server}
|
442
|
+
|
443
|
+
spec{ use{ success = DdeAccessData(data_handle = 123, data_size=zero_id) }}
|
444
|
+
spec{ use{ data_pointer, size = dde_access_data(data_handle = 123) }}
|
445
|
+
|
446
|
+
it "provides access to the data in the specified DDE data handle (both inside and outside of callback)" do
|
447
|
+
setup_server do |*args|
|
448
|
+
@server_calls << extract_values(*args)
|
449
|
+
if args[0] == XTYP_POKE
|
450
|
+
data_handle = args[5]
|
451
|
+
data, size = dde_access_data(data_handle)
|
452
|
+
data.should be_kind_of FFI::Pointer
|
453
|
+
data.read_string.should == POKE_STRING.rstrip
|
454
|
+
size.should == 12
|
455
|
+
buf = FFI::MemoryPointer.new(:int16)
|
456
|
+
data = DdeAccessData(data_handle, buf)
|
457
|
+
buf.get_int16(0).should == 12
|
458
|
+
data.should be_kind_of FFI::Pointer
|
459
|
+
data.read_string.should == POKE_STRING.rstrip
|
460
|
+
dde_unaccess_data(data_handle)
|
461
|
+
end
|
462
|
+
DDE_FACK
|
463
|
+
end
|
464
|
+
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
465
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
466
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
467
|
+
end
|
468
|
+
|
469
|
+
it 'dde data handle expires once transaction is finished (DDE_FACK)' do
|
470
|
+
setup_server do |*args|
|
471
|
+
@server_calls << extract_values(*args);
|
472
|
+
DDE_FACK
|
473
|
+
end
|
474
|
+
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
475
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
476
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
477
|
+
|
478
|
+
# only inside callback block dde data handle is valid (while transaction still in progress)
|
479
|
+
data, size = dde_access_data(@server_calls.last[5])
|
480
|
+
data.should == nil
|
481
|
+
size.should == 0
|
482
|
+
end
|
483
|
+
end # describe dde_access_data
|
484
|
+
|
485
|
+
describe "#dde_unaccess_data" do
|
486
|
+
spec{ use{ success = DdeUnaccessData(data_handle = 123) }}
|
487
|
+
spec{ use{ success = dde_unaccess_data(data_handle = 123) }}
|
488
|
+
|
489
|
+
it "returns 0/false if given invalid DDE data handle " do
|
490
|
+
DdeUnaccessData(data_handle = 123).should == 0
|
491
|
+
dde_unaccess_data(data_handle = 123).should == false
|
492
|
+
end
|
493
|
+
|
494
|
+
it "unaccesses a DDE data handle that was previously accessed" do
|
495
|
+
setup_server do |*args|
|
496
|
+
@server_calls << extract_values(*args);
|
497
|
+
if args[0] == XTYP_POKE
|
498
|
+
data_handle = args[5]
|
499
|
+
dde_unaccess_data(data_handle).should == true
|
500
|
+
|
501
|
+
data, size = dde_access_data(data_handle)
|
502
|
+
data.should be_kind_of FFI::Pointer
|
503
|
+
data.read_string.should == POKE_STRING.rstrip
|
504
|
+
|
505
|
+
dde_unaccess_data(data_handle).should == true
|
506
|
+
end
|
507
|
+
DDE_FACK
|
508
|
+
end
|
509
|
+
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
510
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
511
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
512
|
+
end
|
513
|
+
|
514
|
+
it 'dde data handle expires once transaction is finished (DDE_FACK)' do
|
515
|
+
setup_server do |*args|
|
516
|
+
@server_calls << extract_values(*args);
|
517
|
+
DDE_FACK
|
518
|
+
end
|
519
|
+
@conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
|
520
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
521
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
522
|
+
|
523
|
+
# only inside callback block dde data handle is valid (while transaction still in progress)
|
524
|
+
dde_unaccess_data(@server_calls.last[5]).should == false
|
525
|
+
end
|
526
|
+
|
527
|
+
end # describe dde_unaccess_data
|
528
|
+
end
|
529
529
|
end
|