win 0.3.24 → 0.3.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|