win 0.3.24 → 0.3.25
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +4 -0
- data/README.rdoc +4 -5
- data/VERSION +1 -1
- data/lib/win.rb +1 -1
- data/lib/win/gui/menu.rb +34 -3
- data/lib/win/gui/message.rb +1 -3
- data/lib/win/gui/window.rb +1 -1
- data/lib/win/library.rb +10 -5
- data/lib/win/national.rb +585 -0
- data/lib/win/time.rb +140 -0
- data/spec/extension_spec.rb +2 -38
- data/spec/spec_helper.rb +60 -71
- data/spec/win/dde_spec.rb +435 -437
- data/spec/win/error_spec.rb +86 -88
- data/spec/win/gui/dialog_spec.rb +43 -46
- data/spec/win/gui/input_spec.rb +81 -85
- data/spec/win/gui/menu_spec.rb +260 -247
- data/spec/win/gui/message_spec.rb +218 -219
- data/spec/win/gui/window_spec.rb +558 -557
- data/spec/win/library_spec.rb +180 -199
- data/spec/win/system/info_spec.rb +42 -45
- data/spec/win/system/version_spec.rb +143 -146
- data/spec/win/time_spec.rb +48 -0
- data/tasks/spec.rake +5 -9
- metadata +16 -36
data/lib/win/time.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'win/library'
|
2
|
+
|
3
|
+
module Win
|
4
|
+
|
5
|
+
# Includes functions related to Time in Windows
|
6
|
+
# In general you will want to use this module with Win::National because
|
7
|
+
# it contains the various LOCALE and TIME constants.
|
8
|
+
#
|
9
|
+
# *NB*: Millisecond granularity may NOT be supported by a hardware platform.
|
10
|
+
# The caller of these functions should not rely on more than second granularity.
|
11
|
+
# In fact, granularity of all the GetTime... functions is about ~16msec on Windows.
|
12
|
+
# You'll need to mess with QueryPerformanceFrequency() and QueryPerformanceCounter()
|
13
|
+
# to get real msec granularity on MS Windows;
|
14
|
+
#
|
15
|
+
module Time
|
16
|
+
extend Win::Library
|
17
|
+
TIME_ZONE_ID_UNKNOWN = 0
|
18
|
+
TIME_ZONE_ID_STANDARD = 1
|
19
|
+
TIME_ZONE_ID_DAYLIGHT = 2
|
20
|
+
|
21
|
+
|
22
|
+
##
|
23
|
+
# QueryPerformanceFrequency Function
|
24
|
+
# Retrieves the frequency of the high-resolution performance counter, if one exists.
|
25
|
+
# The frequency cannot change while the system is running.
|
26
|
+
#
|
27
|
+
# [*Syntax*] BOOL WINAPI QueryPerformanceFrequency( lpFrequency );
|
28
|
+
#
|
29
|
+
# lpFrequency <out>
|
30
|
+
# Type: LARGE_INTEGER*
|
31
|
+
# A pointer to a variable that receives the current performance-counter frequency,
|
32
|
+
# in counts per second. If the installed hardware does not support a high-resolution
|
33
|
+
# performance counter, this parameter can be zero.
|
34
|
+
#
|
35
|
+
# *Returns*:: Type: BOOL
|
36
|
+
# If the installed hardware supports a high-resolution performance counter, the return
|
37
|
+
# value is nonzero. If the function fails, the return value is zero. To get extended
|
38
|
+
# error information, call GetLastError. For example, if the installed hardware does
|
39
|
+
# not support a high-resolution performance counter, the function fails.
|
40
|
+
#
|
41
|
+
# ---
|
42
|
+
# <b>Enhanced (snake_case) API: returns high-resolution performance counter frequency
|
43
|
+
# or nil if no high-resolution performance counter is available</b>
|
44
|
+
#
|
45
|
+
# :call-seq:
|
46
|
+
# frequency = query_performance_frequency()
|
47
|
+
#
|
48
|
+
function :QueryPerformanceFrequency, [:pointer], :int8,
|
49
|
+
&->(api){
|
50
|
+
freq = FFI::MemoryPointer.new(:int64)
|
51
|
+
api.call(freq) == 0 ? nil : freq.get_int64(0) }
|
52
|
+
|
53
|
+
##
|
54
|
+
# QueryPerformanceCounter Function
|
55
|
+
# Retrieves the current value of the high-resolution performance counter.
|
56
|
+
#
|
57
|
+
# [*Syntax*] BOOL WINAPI QueryPerformanceCounter( __out LARGE_INTEGER *lpPerformanceCount );
|
58
|
+
#
|
59
|
+
# lpPerformanceCount <out>
|
60
|
+
# Type: LARGE_INTEGER*
|
61
|
+
# A pointer to a variable that receives the current performance-counter value, in counts.
|
62
|
+
#
|
63
|
+
# *Returns*:: Type: BOOL
|
64
|
+
# If the function succeeds, the return value is nonzero.
|
65
|
+
# If the function fails, the return value is zero. To get extended error information,
|
66
|
+
# call GetLastError.
|
67
|
+
# ---
|
68
|
+
# *Remarks*:
|
69
|
+
# On a multiprocessor computer, it should not matter which processor is called. However,
|
70
|
+
# you can get different results on different processors due to bugs in the basic
|
71
|
+
# input/output system (BIOS) or the hardware abstraction layer (HAL). To specify
|
72
|
+
# processor affinity for a thread, use the SetThreadAffinityMask function.
|
73
|
+
# ---
|
74
|
+
# <b>Enhanced (snake_case) API: returns current performance counter value, in counts.
|
75
|
+
# Returns nil if function fails</b>
|
76
|
+
#
|
77
|
+
# :call-seq:
|
78
|
+
# counter = query_performance_counter()
|
79
|
+
#
|
80
|
+
function :QueryPerformanceCounter, [:pointer], :int8,
|
81
|
+
&->(api){
|
82
|
+
count = FFI::MemoryPointer.new(:int64)
|
83
|
+
api.call(count) == 0 ? nil : count.get_int64(0) }
|
84
|
+
|
85
|
+
# Untested
|
86
|
+
|
87
|
+
##
|
88
|
+
function :CompareFileTime, 'PP', 'L'
|
89
|
+
##
|
90
|
+
function :DosDateTimeToFileTime, 'IIP', :int8, boolean: true
|
91
|
+
##
|
92
|
+
function :FileTimeToDosDateTime, 'PPP', :int8, boolean: true
|
93
|
+
##
|
94
|
+
function :FileTimeToLocalFileTime, 'PP', :int8, boolean: true
|
95
|
+
##
|
96
|
+
function :FileTimeToSystemTime, 'PP', :int8, boolean: true
|
97
|
+
##
|
98
|
+
function :GetFileTime, 'LPPP', :int8, boolean: true
|
99
|
+
##
|
100
|
+
# [out] Pointer to a SYSTEMTIME structure to receive the current local date and time.
|
101
|
+
function :GetLocalTime, 'P', :void
|
102
|
+
##
|
103
|
+
# This function retrieves the current system date and time. The system time is expressed in UTC.
|
104
|
+
# [out] Pointer to a SYSTEMTIME structure to receive the current system date and time.
|
105
|
+
function :GetSystemTime, 'P', :void
|
106
|
+
##
|
107
|
+
function :GetSystemTimeAdjustment, 'PPP', :int8, boolean: true
|
108
|
+
##
|
109
|
+
function :GetSystemTimeAsFileTime, 'P', :void
|
110
|
+
##
|
111
|
+
function :GetTickCount, [], :void
|
112
|
+
##
|
113
|
+
function :GetTimeFormat, 'ILPPPI', 'I'
|
114
|
+
##
|
115
|
+
function :GetTimeZoneInformation, 'P', 'L'
|
116
|
+
##
|
117
|
+
function :LocalFileTimeToFileTime, 'PP', :int8, boolean: true
|
118
|
+
##
|
119
|
+
function :SetFileTime, 'LPPP', :int8, boolean: true
|
120
|
+
##
|
121
|
+
function :SetLocalTime, 'P', :int8, boolean: true
|
122
|
+
##
|
123
|
+
function :SetSystemTime, 'P', :int8, boolean: true
|
124
|
+
##
|
125
|
+
function :SetTimeZoneInformation, 'P', :int8, boolean: true
|
126
|
+
##
|
127
|
+
function :SetSystemTimeAdjustment, 'LI', :int8, boolean: true
|
128
|
+
##
|
129
|
+
function :SystemTimeToFileTime, 'PP', :int8, boolean: true
|
130
|
+
##
|
131
|
+
function :SystemTimeToTzSpecificLocalTime, 'PPP', :int8, boolean: true
|
132
|
+
|
133
|
+
##
|
134
|
+
try_function :GetSystemTimes, 'PPP', :int8, boolean: true
|
135
|
+
##
|
136
|
+
try_function :TzSpecificLocalTimeToSystemTime, 'PPP', :int8, boolean: true
|
137
|
+
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
data/spec/extension_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
require 'extension'
|
3
3
|
|
4
4
|
module WinTest
|
@@ -8,7 +8,7 @@ module WinTest
|
|
8
8
|
it 'transforms CamelCase strings' do
|
9
9
|
'GetCharWidth32'.snake_case.should == 'get_char_width_32'
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
it 'leaves snake_case strings intact' do
|
13
13
|
'keybd_event'.snake_case.should == 'keybd_event'
|
14
14
|
end
|
@@ -33,41 +33,5 @@ module WinTest
|
|
33
33
|
'GetCharWidth32'.to_w.bytes.to_a[-2..-1].should == [0, 0]
|
34
34
|
end
|
35
35
|
end
|
36
|
-
|
37
|
-
# context '#to_vkeys' do
|
38
|
-
# it 'transforms number char into [equivalent key code]' do
|
39
|
-
# ('0'..'9').each {|char| char.to_vkeys.should == char.unpack('C')}
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# it 'transforms uppercase letters into [shift, equivalent key code]' do
|
43
|
-
# ('A'..'Z').each {|char| char.to_vkeys.should == [0x10, *char.unpack('C')]}
|
44
|
-
# # Win.const_get(:VK_SHIFT) = 0x10 Bad coupling
|
45
|
-
# end
|
46
|
-
#
|
47
|
-
# it 'transforms lowercase letters into [(upcase) key code]' do
|
48
|
-
# ('a'..'z').each {|char| char.to_vkeys.should == char.upcase.unpack('C')}
|
49
|
-
# end
|
50
|
-
#
|
51
|
-
# it 'transforms space into [equivalent key code]' do
|
52
|
-
# " ".to_vkeys.should == " ".unpack('C')
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# it 'raises error if char is not implemented punctuation' do
|
56
|
-
# ('!'..'/').each {|char| lambda {char.to_vkeys}.should raise_error CONVERSION_ERROR }
|
57
|
-
# (':'..'@').each {|char| lambda {char.to_vkeys}.should raise_error CONVERSION_ERROR }
|
58
|
-
# ('['..'`').each {|char| lambda {char.to_vkeys}.should raise_error CONVERSION_ERROR }
|
59
|
-
# ('{'..'~').each {|char| lambda {char.to_vkeys}.should raise_error CONVERSION_ERROR }
|
60
|
-
# end
|
61
|
-
#
|
62
|
-
# it 'raises error if char is non-printable or non-ascii' do
|
63
|
-
# lambda {1.chr.to_vkeys}.should raise_error CONVERSION_ERROR
|
64
|
-
# lambda {230.chr.to_vkeys}.should raise_error CONVERSION_ERROR
|
65
|
-
# end
|
66
|
-
#
|
67
|
-
# it 'raises error if string is multi-char' do
|
68
|
-
# lambda {'hello'.to_vkeys}.should raise_error CONVERSION_ERROR
|
69
|
-
# lambda {'23'.to_vkeys}.should raise_error CONVERSION_ERROR
|
70
|
-
# end
|
71
|
-
# end
|
72
36
|
end
|
73
37
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,35 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require 'spec/autorun'
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.setup
|
3
|
+
Bundler.require :test
|
5
4
|
require 'win/gui'
|
6
5
|
|
7
6
|
$debug = false
|
8
7
|
|
8
|
+
# Global test methods
|
9
|
+
def cygwin?
|
10
|
+
@cygwin_flag ||= `ruby -v` =~ /cygwin/
|
11
|
+
end
|
12
|
+
|
13
|
+
def os
|
14
|
+
@os_flag ||= cygwin? ? `cmd /c ver` : `ver`
|
15
|
+
end
|
16
|
+
|
17
|
+
def os_2000?
|
18
|
+
os =~ /Version 5.0/
|
19
|
+
end
|
20
|
+
|
21
|
+
def os_xp?
|
22
|
+
os =~ /XP/
|
23
|
+
end
|
24
|
+
|
25
|
+
def os_vista?
|
26
|
+
os =~ /Version 6.0/
|
27
|
+
end
|
28
|
+
|
29
|
+
def os_7?
|
30
|
+
os =~ /Version 6.1/
|
31
|
+
end
|
32
|
+
|
9
33
|
# Customize RSpec with my own extensions
|
10
34
|
module ClassMacros
|
11
35
|
|
@@ -25,7 +49,7 @@ module ClassMacros
|
|
25
49
|
file, line = args
|
26
50
|
end
|
27
51
|
File.open(file) do |f|
|
28
|
-
f.lines.to_a[line.to_i-1].gsub(
|
52
|
+
f.lines.to_a[line.to_i-1].gsub(/(spec.*?{)|(use.*?{)|}/, '').strip
|
29
53
|
end
|
30
54
|
end
|
31
55
|
end
|
@@ -33,86 +57,51 @@ end
|
|
33
57
|
# Customize RSpec with my own extensions
|
34
58
|
module InstanceMacros
|
35
59
|
def use
|
36
|
-
lambda{yield}.should_not raise_error
|
60
|
+
lambda { yield }.should_not raise_error
|
37
61
|
end
|
38
62
|
|
39
63
|
def any_block
|
40
|
-
lambda{|*args| args}
|
64
|
+
lambda { |*args| args }
|
41
65
|
end
|
42
66
|
end
|
43
67
|
|
44
|
-
|
68
|
+
RSpec.configure do |config|
|
45
69
|
config.extend(ClassMacros)
|
46
70
|
config.include(InstanceMacros)
|
47
|
-
|
48
|
-
# class << Spec::ExampleGroup
|
49
|
-
## def spoc &block
|
50
|
-
## it description_from(caller[0]), &block
|
51
|
-
## end
|
52
|
-
# end
|
53
71
|
end
|
54
72
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
73
|
+
KEY_DELAY = 0.001
|
74
|
+
IMPOSSIBLE = 'Impossible'
|
75
|
+
CONVERSION_ERROR = /Can.t convert/
|
76
|
+
SLEEP_DELAY = 0.02
|
77
|
+
APP_PATH = File.join(File.dirname(__FILE__), "../misc/locknote/LockNote.exe")
|
78
|
+
APP_START = cygwin? ? "cmd /c start `cygpath -w #{APP_PATH}`" : 'start "" "' + APP_PATH + '"'
|
79
|
+
WIN_TITLE = 'LockNote - Steganos LockNote'
|
80
|
+
WIN_RECT = [710, 400, 1210, 800]
|
81
|
+
WIN_CLASS = 'ATL:00434098'
|
82
|
+
STATUSBAR_CLASS = 'msctls_statusbar32'
|
83
|
+
TEXTAREA_CLASS = 'ATL:00434310'
|
84
|
+
DESKTOP_CLASS = '#32769'
|
85
|
+
MENU_CLASS = '#32768'
|
86
|
+
|
87
|
+
def any_handle
|
88
|
+
find_window(nil, nil)
|
70
89
|
end
|
71
90
|
|
72
|
-
def
|
73
|
-
|
91
|
+
def not_a_handle
|
92
|
+
123
|
74
93
|
end
|
75
94
|
|
76
|
-
def
|
77
|
-
|
95
|
+
def buffer
|
96
|
+
FFI::MemoryPointer.new(:char, 1024)
|
78
97
|
end
|
79
98
|
|
80
|
-
|
81
|
-
|
82
|
-
KEY_DELAY = 0.001
|
83
|
-
IMPOSSIBLE = 'Impossible'
|
84
|
-
CONVERSION_ERROR = /Can.t convert/
|
85
|
-
SLEEP_DELAY = 0.02
|
86
|
-
APP_PATH = File.join(File.dirname(__FILE__), "../misc/locknote/LockNote.exe" )
|
87
|
-
APP_START = cygwin? ? "cmd /c start `cygpath -w #{APP_PATH}`" : 'start "" "' + APP_PATH + '"'
|
88
|
-
WIN_TITLE = 'LockNote - Steganos LockNote'
|
89
|
-
WIN_RECT = [710, 400, 1210, 800]
|
90
|
-
WIN_CLASS = 'ATL:00434098'
|
91
|
-
STATUSBAR_CLASS = 'msctls_statusbar32'
|
92
|
-
TEXTAREA_CLASS = 'ATL:00434310'
|
93
|
-
DESKTOP_CLASS = '#32769'
|
94
|
-
MENU_CLASS = '#32768'
|
95
|
-
|
96
|
-
def any_handle
|
97
|
-
find_window(nil, nil)
|
98
|
-
end
|
99
|
-
|
100
|
-
def not_a_handle
|
101
|
-
123
|
102
|
-
end
|
103
|
-
|
104
|
-
def buffer
|
105
|
-
FFI::MemoryPointer.new(:char, 1024)
|
106
|
-
end
|
107
|
-
|
108
|
-
def pointer(type=:long, num=1)
|
109
|
-
FFI::MemoryPointer.new(type, num)
|
110
|
-
end
|
99
|
+
def pointer(type=:long, num=1)
|
100
|
+
FFI::MemoryPointer.new(type, num)
|
111
101
|
end
|
112
102
|
|
113
103
|
module WinTestApp
|
114
104
|
|
115
|
-
include WinTest
|
116
105
|
include Win::Gui
|
117
106
|
#include Win::Gui::Convenience
|
118
107
|
|
@@ -120,15 +109,15 @@ module WinTestApp
|
|
120
109
|
system APP_START
|
121
110
|
sleep SLEEP_DELAY until (handle = find_window(nil, WIN_TITLE))
|
122
111
|
|
123
|
-
textarea
|
124
|
-
app
|
112
|
+
textarea = find_window_ex(handle, 0, TEXTAREA_CLASS, nil)
|
113
|
+
app = "Locknote" # App identifier
|
125
114
|
|
126
115
|
eigen_class = class << app;
|
127
116
|
self;
|
128
|
-
end
|
129
|
-
eigen_class.class_eval do
|
130
|
-
define_method(:handle) {handle}
|
131
|
-
define_method(:textarea) {textarea}
|
117
|
+
end # Extracting app's eigenclass
|
118
|
+
eigen_class.class_eval do # Defining singleton methods on app
|
119
|
+
define_method(:handle) { handle }
|
120
|
+
define_method(:textarea) { textarea }
|
132
121
|
end
|
133
122
|
|
134
123
|
@launched_test_app = app
|
data/spec/win/dde_spec.rb
CHANGED
@@ -1,529 +1,527 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
require 'win/dde'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
include WinTest
|
5
|
+
include Win::Dde
|
6
|
+
include Win::Gui::Message
|
7
|
+
|
8
|
+
POKE_STRING = "Poke_string"
|
9
|
+
|
10
|
+
def dde_cmd
|
11
|
+
APPCLASS_STANDARD
|
12
|
+
end
|
13
|
+
|
14
|
+
def dde_callback
|
15
|
+
->(*args){}
|
16
|
+
end
|
17
|
+
|
18
|
+
def zero_id
|
19
|
+
FFI::MemoryPointer.new(:long).write_long(0)
|
20
|
+
end
|
21
|
+
|
22
|
+
def buffer
|
23
|
+
FFI::MemoryPointer.new(:char, 1024)
|
24
|
+
end
|
25
|
+
|
26
|
+
def string_pointer
|
27
|
+
FFI::MemoryPointer.from_string("Pointer_string")
|
28
|
+
end
|
29
|
+
|
30
|
+
def extract_values(*args)
|
31
|
+
type, format, conv, hsz1, hsz2, data, data1, data2 = *args
|
32
|
+
@server_conv = conv
|
33
|
+
[Win::Dde::TYPES[type], format, conv,
|
34
|
+
dde_query_string(@client_id, hsz1),
|
35
|
+
dde_query_string(@client_id, hsz2),
|
36
|
+
data, data1, data2]
|
37
|
+
end
|
38
|
+
|
39
|
+
def setup_server(&server_block)
|
40
|
+
@client_calls = []
|
41
|
+
@server_calls = []
|
42
|
+
@client_id, st = dde_initialize(APPCLASS_STANDARD) { |*args| @client_calls << extract_values(*args); DDE_FACK }
|
43
|
+
@server_id, st = dde_initialize(APPCLASS_STANDARD,
|
44
|
+
&server_block || proc { |*args| @server_calls << extract_values(*args); DDE_FACK })
|
45
|
+
@service_handle = dde_create_string_handle(@server_id, 'service 2', CP_WINANSI)
|
46
|
+
@topic_handle = dde_create_string_handle(@server_id, 'topic 2', CP_WINANSI)
|
47
|
+
dde_name_service(@server_id, @service_handle, DNS_REGISTER)
|
48
|
+
end
|
49
|
+
|
50
|
+
def teardown_server
|
51
|
+
if @print
|
52
|
+
p @server_calls, @client_calls
|
53
|
+
p @server_conv
|
54
|
+
p ERRORS[dde_get_last_error(@server_id)]
|
55
|
+
p ERRORS[dde_get_last_error(@client_id)]
|
56
|
+
@print = nil
|
57
|
+
end
|
58
|
+
dde_name_service(@server_id, @service_handle, DNS_UNREGISTER) if @server_id && @service_handle
|
59
|
+
dde_free_string_handle(@server_id, @service_handle) if @server_id && @service_handle
|
60
|
+
dde_free_string_handle(@server_id, @topic_handle) if @server_id && @topic_handle
|
61
|
+
dde_uninitialize(@server_id) if @server_id
|
62
|
+
dde_uninitialize(@client_id) if @client_id
|
63
|
+
if @conv_handle
|
64
|
+
dde_disconnect(@conv_handle)
|
65
|
+
@conv_handle = nil
|
66
|
+
end
|
67
|
+
@data = nil
|
68
|
+
end
|
8
69
|
|
9
|
-
|
70
|
+
describe Win::Dde, ' contains a set of pre-defined Windows API functions' do
|
10
71
|
|
11
|
-
|
12
|
-
|
13
|
-
|
72
|
+
describe '#register_clipboard_format' do
|
73
|
+
spec { use { RegisterClipboardFormat(format_name = "XlTable") } }
|
74
|
+
spec { use { register_clipboard_format(format_name = "XlTable") } }
|
14
75
|
|
15
|
-
|
16
|
-
|
17
|
-
|
76
|
+
it 'returns format id (int) if successfully registered format' do
|
77
|
+
id = register_clipboard_format("XlTable")
|
78
|
+
id.should_not == 0
|
79
|
+
id.should_not == nil
|
80
|
+
end
|
18
81
|
|
19
|
-
|
20
|
-
|
21
|
-
|
82
|
+
it 'returns same format id for already registered format' do
|
83
|
+
id1 = register_clipboard_format("XlTable")
|
84
|
+
id2 = register_clipboard_format("XlTable")
|
85
|
+
id1.should == id2
|
86
|
+
end
|
22
87
|
|
23
|
-
|
24
|
-
|
88
|
+
it 'returns nil if not able to register format' do
|
89
|
+
register_clipboard_format("").should == nil
|
90
|
+
end
|
25
91
|
end
|
26
92
|
|
27
|
-
|
28
|
-
|
29
|
-
end
|
93
|
+
describe '#dde_initialize' do
|
94
|
+
after(:each) { dde_uninitialize(@id) if @id }
|
30
95
|
|
31
|
-
|
32
|
-
|
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
|
96
|
+
spec { use { status = DdeInitialize(id = zero_id, dde_callback, dde_cmd, unused=0); @id = id.read_long } }
|
97
|
+
spec { use { @id, status = dde_initialize(@id=0, dde_cmd, &dde_callback) } }
|
39
98
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
99
|
+
it 'with zero instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
|
100
|
+
@id, status = dde_initialize(0, APPCLASS_STANDARD) { |*args|}
|
101
|
+
@id.should be_an Integer
|
102
|
+
@id.should_not == 0
|
103
|
+
status.should == DMLERR_NO_ERROR
|
104
|
+
end
|
50
105
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
p ERRORS[dde_get_last_error(@client_id)]
|
57
|
-
@print = nil
|
106
|
+
it 'with nil instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
|
107
|
+
@id, status = dde_initialize(nil, APPCLASS_STANDARD) { |*args|}
|
108
|
+
@id.should be_an Integer
|
109
|
+
@id.should_not == 0
|
110
|
+
status.should == DMLERR_NO_ERROR
|
58
111
|
end
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
dde_disconnect(@conv_handle)
|
66
|
-
@conv_handle = nil
|
112
|
+
|
113
|
+
it 'with omitted instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
|
114
|
+
@id, status = dde_initialize(APPCLASS_STANDARD) { |*args|}
|
115
|
+
@id.should be_an Integer
|
116
|
+
@id.should_not == 0
|
117
|
+
status.should == DMLERR_NO_ERROR
|
67
118
|
end
|
68
|
-
@data = nil
|
69
|
-
end
|
70
119
|
|
71
|
-
|
120
|
+
it 'returns error status if initialization unsuccessful' do
|
121
|
+
@id, status = dde_initialize(12345, APPCLASS_STANDARD) { |*args|}
|
122
|
+
status.should == DMLERR_INVALIDPARAMETER
|
123
|
+
@id.should == nil
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'is able to reinitialize with correct id' do
|
127
|
+
@id, status = dde_initialize(APPCLASS_STANDARD) { |*args|}
|
128
|
+
new_id, status = dde_initialize(@id, APPCLASS_STANDARD) { |*args|}
|
129
|
+
status.should == DMLERR_NO_ERROR
|
130
|
+
new_id.should == @id
|
131
|
+
end
|
132
|
+
end # describe 'dde_initialize'
|
72
133
|
|
73
|
-
|
74
|
-
|
75
|
-
|
134
|
+
context 'after initialization:' do
|
135
|
+
before(:each) { @id, status = dde_initialize(APPCLASS_STANDARD) { |*args|} }
|
136
|
+
after(:each) { dde_uninitialize(@id) if @id }
|
76
137
|
|
77
|
-
|
78
|
-
id = register_clipboard_format("XlTable")
|
79
|
-
id.should_not == 0
|
80
|
-
id.should_not == nil
|
81
|
-
end
|
138
|
+
describe '#dde_uninitialize' do
|
82
139
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
140
|
+
spec { use { status = DdeUninitialize(@id) } }
|
141
|
+
spec { use { id, status = dde_uninitialize(@id) } }
|
142
|
+
|
143
|
+
it 'returns true if uninitialization successful' do
|
144
|
+
res = dde_uninitialize(@id)
|
145
|
+
res.should == true
|
87
146
|
end
|
88
147
|
|
89
|
-
it 'returns
|
90
|
-
|
148
|
+
it 'returns false if initialization unsuccessful' do
|
149
|
+
res = dde_uninitialize(12345)
|
150
|
+
res.should == false
|
91
151
|
end
|
92
|
-
end
|
152
|
+
end # describe '#dde_uninitialize'
|
93
153
|
|
94
|
-
describe '#
|
95
|
-
after(:each) {
|
154
|
+
describe '#dde_create_string_handle' do
|
155
|
+
after(:each) { dde_free_string_handle(@id, @string_handle) if @string_handle }
|
96
156
|
|
97
|
-
spec{ use{
|
98
|
-
spec{ use{ @
|
157
|
+
spec { use { @string_handle = DdeCreateStringHandle(id=0, string_pointer, code_page_id=CP_WINANSI) } }
|
158
|
+
spec { use { @string_handle = dde_create_string_handle(id=0, string='Any String', code_page_id=CP_WINANSI) } }
|
99
159
|
|
100
|
-
it '
|
101
|
-
@
|
102
|
-
@
|
103
|
-
@
|
104
|
-
status.should == DMLERR_NO_ERROR
|
160
|
+
it 'returns nonzero Integer handle to a string (passable to other DDEML functions)' do
|
161
|
+
@string_handle = dde_create_string_handle(@id, 'My String', CP_WINANSI)
|
162
|
+
@string_handle.should be_an Integer
|
163
|
+
@string_handle.should_not == 0
|
105
164
|
end
|
106
165
|
|
107
|
-
it '
|
108
|
-
@
|
109
|
-
@
|
110
|
-
@
|
111
|
-
status.should == DMLERR_NO_ERROR
|
166
|
+
it 'creates handle even if code_page is omitted' do
|
167
|
+
@string_handle = dde_create_string_handle(@id, 'My String')
|
168
|
+
@string_handle.should be_an Integer
|
169
|
+
@string_handle.should_not == 0
|
112
170
|
end
|
113
171
|
|
114
|
-
it '
|
115
|
-
@
|
116
|
-
|
117
|
-
|
118
|
-
|
172
|
+
it 'creating two handles for the SAME string (inside one instance) USUALLY returns same handle' do
|
173
|
+
@string_handle = dde_create_string_handle(@id, 'My String')
|
174
|
+
10.times do
|
175
|
+
string_handle1 = dde_create_string_handle(@id, 'My String')
|
176
|
+
string_handle1.should == @string_handle
|
177
|
+
dde_free_string_handle(@id, string_handle1)
|
178
|
+
end
|
119
179
|
end
|
120
180
|
|
121
|
-
it '
|
122
|
-
@
|
123
|
-
|
124
|
-
|
181
|
+
it 'created different handles for two different strings ' do
|
182
|
+
@string_handle = dde_create_string_handle(@id, 'My String')
|
183
|
+
string_handle1 = dde_create_string_handle(@id, 'My String1')
|
184
|
+
string_handle1.should_not == @string_handle
|
185
|
+
dde_free_string_handle(@id, string_handle1)
|
125
186
|
end
|
126
187
|
|
127
|
-
it '
|
128
|
-
@
|
129
|
-
|
130
|
-
status.should == DMLERR_NO_ERROR
|
131
|
-
new_id.should == @id
|
188
|
+
it 'returns nil if unable to register handle to a string' do
|
189
|
+
@string_handle = dde_create_string_handle(@id, "", CP_WINANSI)
|
190
|
+
@string_handle.should == nil
|
132
191
|
end
|
133
|
-
end # describe '
|
192
|
+
end # describe '#dde_create_string_handle'
|
134
193
|
|
135
|
-
context '
|
136
|
-
before(:each) {@id,
|
137
|
-
after(:each) {
|
194
|
+
context "with dde string handle to 'My String 2'" do
|
195
|
+
before(:each) { @string_handle = dde_create_string_handle(@id, 'My String 2', CP_WINANSI) }
|
196
|
+
after(:each) { dde_free_string_handle(@id, @string_handle) }
|
138
197
|
|
139
|
-
describe '#
|
198
|
+
describe '#dde_query_string' do
|
140
199
|
|
141
|
-
spec{ use{
|
142
|
-
spec{ use{
|
200
|
+
spec { use { string = DdeQueryString(@id, @string_handle, buffer, buffer.size, code_page=CP_WINANSI) } }
|
201
|
+
spec { use { string = dde_query_string(@id, @string_handle, code_page=CP_WINANSI) } }
|
143
202
|
|
144
|
-
it '
|
145
|
-
|
146
|
-
|
203
|
+
it 'retrieves string that given string handle refers to' do
|
204
|
+
num_chars = DdeQueryString(@id, @string_handle, buf = buffer, buf.size, CP_WINANSI)
|
205
|
+
num_chars.should == 11
|
206
|
+
buf.read_string.should == 'My String 2'
|
147
207
|
end
|
148
208
|
|
149
|
-
it '
|
150
|
-
|
151
|
-
|
209
|
+
it 'retrieves string that given string handle refers to' do
|
210
|
+
string = dde_query_string(@id, @string_handle, CP_WINANSI)
|
211
|
+
string.should == 'My String 2'
|
152
212
|
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
213
|
|
158
|
-
|
159
|
-
|
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
|
214
|
+
it 'retrieves string even if code_page is omitted' do
|
215
|
+
string = dde_query_string(@id, @string_handle)
|
216
|
+
string.should == 'My String 2'
|
165
217
|
end
|
166
218
|
|
167
|
-
it '
|
168
|
-
|
169
|
-
|
170
|
-
@string_handle.should_not == 0
|
219
|
+
it 'returns nil attempting to retrieve invalid handle' do
|
220
|
+
string = dde_query_string(@id, 12345)
|
221
|
+
string.should == nil
|
171
222
|
end
|
223
|
+
end # describe '#dde_query_string'
|
172
224
|
|
173
|
-
|
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
|
225
|
+
describe '#dde_free_string_handle' do
|
181
226
|
|
182
|
-
|
183
|
-
|
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
|
227
|
+
spec { use { success = DdeFreeStringHandle(@id, @string_handle) } }
|
228
|
+
spec { use { success = dde_free_string_handle(@id, @string_handle) } }
|
188
229
|
|
189
|
-
it 'returns
|
190
|
-
|
191
|
-
|
230
|
+
it 'returns true when freeing string handle registered with DDEML' do
|
231
|
+
res = dde_free_string_handle(@id, @string_handle)
|
232
|
+
res.should == true
|
192
233
|
end
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
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
|
234
|
+
|
235
|
+
it 'returns false attempting to free unregistered handle' do
|
236
|
+
res = dde_free_string_handle(@id, 12345)
|
237
|
+
res.should == false
|
293
238
|
end
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
@
|
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
|
239
|
+
|
240
|
+
it 'keeps string accessible while references to it still exist' do
|
241
|
+
# creates second handle to 'My String 2'
|
242
|
+
string_handle_1 = dde_create_string_handle(@id, 'My String 2', CP_WINANSI)
|
243
|
+
|
244
|
+
dde_free_string_handle(@id, @string_handle)
|
245
|
+
dde_query_string(@id, @string_handle).should == 'My String 2'
|
246
|
+
|
247
|
+
dde_free_string_handle(@id, string_handle_1)
|
248
|
+
dde_query_string(@id, @string_handle).should == nil
|
311
249
|
end
|
312
250
|
|
313
|
-
it '
|
314
|
-
|
315
|
-
|
316
|
-
puts @conv_handle
|
251
|
+
it 'makes string inaccessible once its last handle is freed' do
|
252
|
+
dde_free_string_handle(@id, @string_handle)
|
253
|
+
dde_query_string(@id, @string_handle).should == nil
|
317
254
|
end
|
318
|
-
end # describe '#
|
255
|
+
end # describe '#dde_free_string_handle'
|
319
256
|
|
320
|
-
describe '#
|
321
|
-
spec{ use{
|
322
|
-
spec{ use{
|
257
|
+
describe '#dde_get_last_error' do
|
258
|
+
spec { use { error_code = DdeGetLastError(@id) } }
|
259
|
+
spec { use { error_code = dde_get_last_error(@id) } }
|
323
260
|
|
324
|
-
it '
|
325
|
-
|
261
|
+
it 'original API returns DMLERR_NO_ERROR if there is no last DDE error for given app instance' do
|
262
|
+
DdeGetLastError(@id).should == DMLERR_NO_ERROR
|
326
263
|
end
|
327
264
|
|
328
|
-
it '
|
329
|
-
|
330
|
-
dde_disconnect(conv_handle).should == true
|
265
|
+
it 'snake_case API returns nil if there is no last DDE error for given app instance' do
|
266
|
+
dde_get_last_error(@id).should == nil
|
331
267
|
end
|
332
268
|
|
333
|
-
it '
|
334
|
-
|
335
|
-
|
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)]
|
269
|
+
it 'returns error code of last DDE error for given app instance' do
|
270
|
+
dde_name_service(@id, 1234, DNS_REGISTER)
|
271
|
+
dde_get_last_error(@id).should == DMLERR_INVALIDPARAMETER
|
341
272
|
end
|
342
|
-
end # describe '#
|
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}
|
273
|
+
end # describe '#dde_get_last_error'
|
356
274
|
|
357
|
-
|
358
|
-
|
275
|
+
end # context "with dde string handle to 'My String'"
|
276
|
+
end # context 'after initialization:'
|
359
277
|
|
278
|
+
context 'with synthetic DDE client/server' do
|
279
|
+
before(:each) { setup_server }
|
280
|
+
after(:each) { teardown_server }
|
360
281
|
|
361
|
-
|
362
|
-
|
363
|
-
|
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
|
282
|
+
describe '#dde_name_service' do
|
283
|
+
spec { use { success = dde_name_service(@server_id, @service_handle, cmd=DNS_UNREGISTER) } }
|
284
|
+
spec { use { success = DdeNameService(@server_id, @service_handle, reserved=0, cmd=DNS_UNREGISTER) } }
|
370
285
|
|
371
|
-
it
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
@
|
286
|
+
it 'registers or unregisters the service names that DDE server supports' do
|
287
|
+
pending 'Register/Unregister messages don`t show up in @server_calls :('
|
288
|
+
success = dde_name_service(@server_id, @service_handle, DNS_REGISTER)
|
289
|
+
success.should == true
|
290
|
+
success = dde_name_service(@server_id, @service_handle, DNS_UNREGISTER)
|
291
|
+
success.should == true
|
292
|
+
end
|
293
|
+
end # describe '#dde_name_service'
|
294
|
+
|
295
|
+
describe '#dde_connect' do
|
296
|
+
spec { use { @conv_handle = DdeConnect(instance_id=0, service=0, topic=0, context=nil) } }
|
297
|
+
spec { use { @conv_handle = dde_connect(instance_id=0, service=0, topic=0, context=nil) } }
|
298
|
+
|
299
|
+
it 'connects to existing DDE server (self in this case)' do
|
300
|
+
@conv_handle = dde_connect(@server_id, @service_handle, @topic_handle, context=nil)
|
301
|
+
|
302
|
+
@server_calls.first[0].should == 'XTYP_CONNECT'
|
303
|
+
@server_calls.first[3].should == 'topic 2'
|
304
|
+
@server_calls.first[4].should == 'service 2'
|
305
|
+
@server_calls[1][0].should == 'XTYP_CONNECT_CONFIRM'
|
306
|
+
@server_calls[1][3].should == 'topic 2'
|
307
|
+
@server_calls[1][4].should == 'service 2'
|
308
|
+
dde_disconnect(@server_conv).should == true
|
309
|
+
dde_disconnect(@conv_handle).should == true
|
376
310
|
end
|
377
311
|
|
378
|
-
it
|
379
|
-
|
380
|
-
|
381
|
-
@
|
382
|
-
@data_out.read_string.should == POKE_STRING.rstrip
|
312
|
+
it 'connects to existing DDE server (from @client, NOT self)' do
|
313
|
+
pending 'something is wrong when connecting to separate service instance, uninitialize fails'
|
314
|
+
@conv_handle = dde_connect(@client_id, @service_handle, @topic_handle, context=nil)
|
315
|
+
puts @conv_handle
|
383
316
|
end
|
384
|
-
end # describe
|
317
|
+
end # describe '#dde_connect'
|
385
318
|
|
386
|
-
describe '#
|
387
|
-
|
319
|
+
describe '#dde_disconnect' do
|
320
|
+
spec { use { success = DdeDisconnect(conversation_handle=0) } }
|
321
|
+
spec { use { success = dde_disconnect(conversation_handle=0) } }
|
388
322
|
|
389
|
-
|
390
|
-
|
391
|
-
|
323
|
+
it 'fails to disconnect if not valid conversation handle given' do
|
324
|
+
dde_disconnect(12345).should == false
|
325
|
+
end
|
392
326
|
|
393
|
-
it '
|
394
|
-
|
327
|
+
it 'disconnects from existing (self) DDE server' do
|
328
|
+
conv_handle = dde_connect(@server_id, @service_handle, @topic_handle, context=nil)
|
329
|
+
dde_disconnect(conv_handle).should == true
|
395
330
|
end
|
396
331
|
|
397
|
-
it '
|
398
|
-
|
399
|
-
|
332
|
+
it 'disconnects from existing (self) DDE server' do
|
333
|
+
pending 'XTYP_DISCONNECT is not received by server callback (since we are disconnecting from self?)'
|
334
|
+
conv_handle = dde_connect(@server_id, @service_handle, @topic_handle, context=nil)
|
335
|
+
dde_disconnect(conv_handle).should == true
|
336
|
+
@server_calls.last[0].should == "XTYP_DISCONNECT"
|
337
|
+
p @server_calls, @client_calls, @server_conv
|
338
|
+
p ERRORS[dde_get_last_error(@server_id)]
|
339
|
+
p ERRORS[dde_get_last_error(@client_id)]
|
400
340
|
end
|
341
|
+
end # describe '#dde_disconnect'
|
342
|
+
end # context 'with synthetic DDE server'
|
343
|
+
|
344
|
+
describe "#dde_client_transaction" do
|
345
|
+
before(:each) do
|
346
|
+
setup_server do |*args|
|
347
|
+
@server_calls << extract_values(*args)
|
348
|
+
@data_out, size = dde_get_data(args[5]) if args.first == XTYP_POKE || args.first == XTYP_EXECUTE
|
349
|
+
DDE_FACK
|
350
|
+
end
|
351
|
+
@conv_handle = dde_connect(@server_id, @service_handle, @topic_handle, context=nil)
|
352
|
+
@data_in = FFI::MemoryPointer.from_string POKE_STRING
|
353
|
+
end
|
354
|
+
after(:each) { teardown_server }
|
355
|
+
|
356
|
+
spec { use { DdeClientTransaction(data=nil, size=0, conv=0, item=0, format=0, type=0, timeout=0, result=nil) } }
|
357
|
+
spec { use { dde_client_transaction(data=nil, size=0, conv=0, item=0, format=0, type=0, timeout=0, result=nil) } }
|
358
|
+
|
401
359
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
360
|
+
it "returns 0/nil if initiated transaction unsuccessful" do
|
361
|
+
res = DdeClientTransaction(@data_in, @data_in.size, 1234, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
362
|
+
res.should == 0 # wrong conversation handle
|
363
|
+
res = dde_client_transaction(@data_in, @data_in.size, 1234, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
364
|
+
res.should == nil # wrong conversation handle
|
365
|
+
res = dde_client_transaction(@data_in, @data_in.size, @conv_handle, 0, CF_TEXT, XTYP_POKE, 1000, nil)
|
366
|
+
res.should == nil # wrong item handle (cannot be NULL in XTYP_POKE transaction)
|
367
|
+
@server_calls.any? { |call| call[0] == 'XTYP_POKE' }.should be_false
|
368
|
+
end
|
369
|
+
|
370
|
+
it "original api is used by CLIENT to begins a data transaction with server" do
|
371
|
+
res = DdeClientTransaction(@data_in, @data_in.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
372
|
+
res.should == 1
|
373
|
+
@server_calls.any? { |call| call[0] == 'XTYP_POKE' }.should be_true
|
374
|
+
@data_out.read_string.should == POKE_STRING.rstrip
|
375
|
+
end
|
376
|
+
|
377
|
+
it "snake_case api begins a data transaction between a client and a server" do
|
378
|
+
res = dde_client_transaction(@data_in, @data_in.size, @conv_handle, 0, 0, XTYP_EXECUTE, 1000, nil)
|
379
|
+
res.should be_true
|
380
|
+
@server_calls.any? { |call| call[0] == 'XTYP_EXECUTE' }.should be_true
|
381
|
+
@data_out.read_string.should == POKE_STRING.rstrip
|
382
|
+
end
|
383
|
+
end # describe dde_client_transaction
|
384
|
+
|
385
|
+
describe '#dde_get_data' do
|
386
|
+
after(:each) { teardown_server }
|
387
|
+
|
388
|
+
spec { use { data_pointer, size = dde_get_data(data_handle = 123, max = 1073741823, offset = 0) } }
|
389
|
+
spec { use { size = DdeGetData(data_handle = 123, nil, 0, 0) } } # returns dde data set size in bytes
|
390
|
+
spec { use { size = DdeGetData(data_handle = 123, FFI::MemoryPointer.new(:char, 1024), max = 1024, offset = 0) } }
|
391
|
+
|
392
|
+
it 'original API returns 0 if trying to address invalid dde data handle' do
|
393
|
+
DdeGetData(data_handle = 123, nil, 0, 0).should == 0
|
394
|
+
end
|
395
|
+
|
396
|
+
it 'snake_case API returns [nil, 0] if trying to address invalid dde data handle' do
|
397
|
+
data, size = dde_get_data(data_handle = 123, 3741823, 0)
|
398
|
+
data.should == nil
|
399
|
+
end
|
400
|
+
|
401
|
+
it 'returns dde data if used inside dde callback block' do
|
402
|
+
setup_server do |*args|
|
403
|
+
@server_calls << extract_values(*args);
|
404
|
+
if args[0] == XTYP_POKE
|
405
|
+
data_handle = args[5]
|
406
|
+
data, size = dde_get_data(data_handle)
|
407
|
+
data.should be_an FFI::MemoryPointer
|
408
|
+
data.read_string.should == POKE_STRING.rstrip
|
409
|
+
size.should == 12
|
410
|
+
DdeGetData(data_handle, nil, 0, 0).should == 12
|
411
|
+
data = FFI::MemoryPointer.new(:char, 1024)
|
412
|
+
DdeGetData(data_handle, data, data.size, 0).should == 12
|
413
|
+
data.read_string.should == POKE_STRING.rstrip
|
417
414
|
end
|
418
|
-
|
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)
|
415
|
+
DDE_FACK
|
421
416
|
end
|
417
|
+
@conv_handle = dde_connect(@server_id, @service_handle, @topic_handle, context=nil)
|
418
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
419
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
420
|
+
end
|
422
421
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
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
|
422
|
+
it 'dde data handle expires once transaction is finished (DDE_FACK)' do
|
423
|
+
setup_server do |*args|
|
424
|
+
@server_calls << extract_values(*args);
|
425
|
+
DDE_FACK
|
436
426
|
end
|
427
|
+
@conv_handle = dde_connect(@server_id, @service_handle, @topic_handle, context=nil)
|
428
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
429
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
430
|
+
|
431
|
+
# only inside callback block dde data handle is valid (while transaction still in progress)
|
432
|
+
data, size = dde_get_data(@server_calls.last[5])
|
433
|
+
data.should == nil
|
434
|
+
size.should == 0
|
435
|
+
end
|
437
436
|
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
end
|
462
|
-
DDE_FACK
|
437
|
+
end # describe '#dde_get_data'
|
438
|
+
|
439
|
+
describe "#dde_access_data" do
|
440
|
+
after(:each) { teardown_server }
|
441
|
+
|
442
|
+
spec { use { success = DdeAccessData(data_handle = 123, data_size=zero_id) } }
|
443
|
+
spec { use { data_pointer, size = dde_access_data(data_handle = 123) } }
|
444
|
+
|
445
|
+
it "provides access to the data in the specified DDE data handle (both inside and outside of callback)" do
|
446
|
+
setup_server do |*args|
|
447
|
+
@server_calls << extract_values(*args)
|
448
|
+
if args[0] == XTYP_POKE
|
449
|
+
data_handle = args[5]
|
450
|
+
data, size = dde_access_data(data_handle)
|
451
|
+
data.should be_kind_of FFI::Pointer
|
452
|
+
data.read_string.should == POKE_STRING.rstrip
|
453
|
+
size.should == 12
|
454
|
+
buf = FFI::MemoryPointer.new(:int16)
|
455
|
+
data = DdeAccessData(data_handle, buf)
|
456
|
+
buf.get_int16(0).should == 12
|
457
|
+
data.should be_kind_of FFI::Pointer
|
458
|
+
data.read_string.should == POKE_STRING.rstrip
|
459
|
+
dde_unaccess_data(data_handle)
|
463
460
|
end
|
464
|
-
|
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)
|
461
|
+
DDE_FACK
|
467
462
|
end
|
463
|
+
@conv_handle = dde_connect(@server_id, @service_handle, @topic_handle, context=nil)
|
464
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
465
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
466
|
+
end
|
468
467
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
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
|
468
|
+
it 'dde data handle expires once transaction is finished (DDE_FACK)' do
|
469
|
+
setup_server do |*args|
|
470
|
+
@server_calls << extract_values(*args);
|
471
|
+
DDE_FACK
|
482
472
|
end
|
483
|
-
|
473
|
+
@conv_handle = dde_connect(@server_id, @service_handle, @topic_handle, context=nil)
|
474
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
475
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
476
|
+
|
477
|
+
# only inside callback block dde data handle is valid (while transaction still in progress)
|
478
|
+
data, size = dde_access_data(@server_calls.last[5])
|
479
|
+
data.should == nil
|
480
|
+
size.should == 0
|
481
|
+
end
|
482
|
+
end # describe dde_access_data
|
484
483
|
|
485
|
-
|
486
|
-
|
487
|
-
|
484
|
+
describe "#dde_unaccess_data" do
|
485
|
+
spec { use { success = DdeUnaccessData(data_handle = 123) } }
|
486
|
+
spec { use { success = dde_unaccess_data(data_handle = 123) } }
|
488
487
|
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
488
|
+
it "returns 0/false if given invalid DDE data handle " do
|
489
|
+
DdeUnaccessData(data_handle = 123).should == 0
|
490
|
+
dde_unaccess_data(data_handle = 123).should == false
|
491
|
+
end
|
493
492
|
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
493
|
+
it "unaccesses a DDE data handle that was previously accessed" do
|
494
|
+
setup_server do |*args|
|
495
|
+
@server_calls << extract_values(*args);
|
496
|
+
if args[0] == XTYP_POKE
|
497
|
+
data_handle = args[5]
|
498
|
+
dde_unaccess_data(data_handle).should == true
|
500
499
|
|
501
|
-
|
502
|
-
|
503
|
-
|
500
|
+
data, size = dde_access_data(data_handle)
|
501
|
+
data.should be_kind_of FFI::Pointer
|
502
|
+
data.read_string.should == POKE_STRING.rstrip
|
504
503
|
|
505
|
-
|
506
|
-
end
|
507
|
-
DDE_FACK
|
504
|
+
dde_unaccess_data(data_handle).should == true
|
508
505
|
end
|
509
|
-
|
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)
|
506
|
+
DDE_FACK
|
512
507
|
end
|
508
|
+
@conv_handle = dde_connect(@server_id, @service_handle, @topic_handle, context=nil)
|
509
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
510
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
511
|
+
end
|
513
512
|
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
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
|
513
|
+
it 'dde data handle expires once transaction is finished (DDE_FACK)' do
|
514
|
+
setup_server do |*args|
|
515
|
+
@server_calls << extract_values(*args);
|
516
|
+
DDE_FACK
|
525
517
|
end
|
518
|
+
@conv_handle = dde_connect(@server_id, @service_handle, @topic_handle, context=nil)
|
519
|
+
str = FFI::MemoryPointer.from_string POKE_STRING
|
520
|
+
dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
|
526
521
|
|
527
|
-
|
528
|
-
|
529
|
-
end
|
522
|
+
# only inside callback block dde data handle is valid (while transaction still in progress)
|
523
|
+
dde_unaccess_data(@server_calls.last[5]).should == false
|
524
|
+
end
|
525
|
+
|
526
|
+
end # describe dde_unaccess_data
|
527
|
+
end
|