win 0.1.27 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|