nwrfc 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +85 -0
- data/Rakefile +8 -0
- data/lib/dev_rfc.trc +133 -0
- data/lib/nwrfc.old.rb +338 -0
- data/lib/nwrfc.rb +409 -0
- data/lib/nwrfc/nwrfclib.rb +449 -0
- metadata +71 -0
data/lib/nwrfc.rb
ADDED
@@ -0,0 +1,409 @@
|
|
1
|
+
# Author:: Martin Ceronio martin.ceronio@infosize.co.za
|
2
|
+
# Copyright:: Copyright (c) 2012 Martin Ceronio
|
3
|
+
# License:: MIT and/or Creative Commons Attribution-ShareAlike
|
4
|
+
# SAP, Netweaver, RFC and other names referred to in this code
|
5
|
+
# are, or may be registered trademarks and the property of SAP, AG
|
6
|
+
# No ownership over any of these is asserted by Martin Ceronio
|
7
|
+
|
8
|
+
require File.dirname(__FILE__)+'/nwrfc/nwrfclib'
|
9
|
+
|
10
|
+
require 'date'
|
11
|
+
require 'time'
|
12
|
+
|
13
|
+
# This library provides a way to call the functions of the SAP Netweaver RFC
|
14
|
+
# SDK, i.e. opening a connection to an ABAP system, calling functions etc., as
|
15
|
+
# well as running an RFC service
|
16
|
+
#---
|
17
|
+
# *TODO*: Create an error class that wraps the SAP error struct, so it can
|
18
|
+
# be raised and the caller can get all the information from there
|
19
|
+
#+++
|
20
|
+
|
21
|
+
module NWRFC
|
22
|
+
|
23
|
+
NW_TIME_FORMAT = "%H%M%S"
|
24
|
+
NW_DATE_FORMAT = "%Y%m%d"
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
self.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def NWRFC.get_version
|
31
|
+
# See custom method FFI::Pointer#read_string_dn in nwrfclib.rb
|
32
|
+
# http://stackoverflow.com/questions/9293307/ruby-ffi-ruby-1-8-reading-utf-16le-encoded-strings
|
33
|
+
major = FFI::MemoryPointer.new(:uint)
|
34
|
+
minor = FFI::MemoryPointer.new(:uint)
|
35
|
+
patch = FFI::MemoryPointer.new(:uint)
|
36
|
+
version = NWRFCLib.get_version(major, minor, patch)
|
37
|
+
[version.read_string_dn.uC, major.read_uint, minor.read_uint, patch.read_uint]
|
38
|
+
end
|
39
|
+
|
40
|
+
def NWRFC.check_error(error_handle)
|
41
|
+
raise "Error code #{error_handle[:code]} group #{error_handle[:group]} message #{error_handle[:message].get_str}" \
|
42
|
+
if error_handle[:code] > 0
|
43
|
+
end
|
44
|
+
|
45
|
+
# Represents a connection to a SAP system that can be used to invoke
|
46
|
+
# remote-enabled functions
|
47
|
+
class Connection
|
48
|
+
attr_reader :handle
|
49
|
+
|
50
|
+
# Opens a connection to the SAP system with the given connection parameters
|
51
|
+
# (described in the NW RFC SDK document), passed in the form of a Hash, e.g.
|
52
|
+
# Connection.new { 'ashost' :=> 'ajax.domain.com', ... }
|
53
|
+
def initialize(conn_params)
|
54
|
+
conn_params.untaint #For params loaded from file, e.g.
|
55
|
+
raise "Connection parameters must be a Hash" unless conn_params.instance_of? Hash
|
56
|
+
#NWRFCLib.init
|
57
|
+
@cparams = NWRFCLib.make_conn_params(conn_params)
|
58
|
+
raise "Could not create valid pointer from parameters" unless @cparams.instance_of? FFI::MemoryPointer
|
59
|
+
#@errp = FFI::MemoryPointer.new(NWRFCLib::RFCError)
|
60
|
+
@error = NWRFCLib::RFCError.new #@errp
|
61
|
+
@handle = NWRFCLib.open_connection(@cparams, conn_params.length, @error.to_ptr)
|
62
|
+
NWRFC.check_error(@error)
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
# Call the NW RFC SDK's RfcCloseConnection() function with the current
|
67
|
+
# connection; this (should - *TODO* - check) invalidate the connection handle
|
68
|
+
# and cause an error on any subsequent use of this connection
|
69
|
+
def disconnect
|
70
|
+
NWRFCLib.close_connection(@handle, @error.to_ptr)
|
71
|
+
NWRFC.check_error(@error)
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_function(function_name)
|
75
|
+
Function.new(self, function_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Return details about the current connection and the system
|
79
|
+
def connection_info
|
80
|
+
return @get_connection_attributes if @get_connection_attributes
|
81
|
+
conn_info = NWRFCLib::RFCConnection.new
|
82
|
+
rc = NWRFCLib.get_connection_attributes(@handle, conn_info.to_ptr, @error)
|
83
|
+
NWRFC.check_error(@error) if rc > 0
|
84
|
+
@get_connection_attributes = conn_info.members.inject({}) {|hash, member|
|
85
|
+
hash[member] = conn_info[member].get_str #get_str, own definition in nwrfclib.rb, FFI::StructLayout::CharArray#get_str
|
86
|
+
hash
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
# Represents a remote-enabled function module for RFC, can be instantiated either by the caller
|
93
|
+
# or by calling Connection#get_function. This only represents the description of the function;
|
94
|
+
# to call a function, an instance of a function call must be obtained with #get_function_call
|
95
|
+
class Function
|
96
|
+
attr_reader :desc, :connection, :function_name
|
97
|
+
|
98
|
+
# Get a function module instance; can also be obtained by calling Connection#get_function
|
99
|
+
def initialize(connection, function_name)
|
100
|
+
@function_name = function_name
|
101
|
+
@error = NWRFCLib::RFCError.new
|
102
|
+
@desc = NWRFCLib.get_function_desc(connection.handle, function_name.cU, @error.to_ptr)
|
103
|
+
@connection = connection
|
104
|
+
NWRFC.check_error(@error)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Create and return a callable instance of this function module
|
108
|
+
def get_function_call
|
109
|
+
FunctionCall.new(self)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Get the number of parameters this function has
|
113
|
+
def parameter_count
|
114
|
+
pcount = FFI::MemoryPointer.new(:uint)
|
115
|
+
rc = NWRFCLib.get_parameter_count(@desc, pcount, @error)
|
116
|
+
NWRFC.check_error(@error) if rc > 0
|
117
|
+
pcount.read_uint
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
# Representation of a data container (function, structure or table)
|
123
|
+
class DataContainer
|
124
|
+
attr_reader :handle, :desc
|
125
|
+
|
126
|
+
def initialize(handle)
|
127
|
+
@error = NWRFCLib::RFCError.new
|
128
|
+
@handle = handle
|
129
|
+
@desc = NWRFCLib.describe_type(@handle, @error)
|
130
|
+
@member_metadata = {} #Cache of metadata for members
|
131
|
+
NWRFC.check_error(@error)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Return the member specified by string or symbol
|
135
|
+
def [](element)
|
136
|
+
member = element.to_s.upcase
|
137
|
+
metadata = member_metadata(element)
|
138
|
+
case metadata[:type]
|
139
|
+
when :RFCTYPE_CHAR
|
140
|
+
# TODO: Try use :string parameter in get_chars
|
141
|
+
return read_chars(metadata)
|
142
|
+
when :RFCTYPE_DATE
|
143
|
+
return Date.parse(read_chars(metadata))
|
144
|
+
#return Date.new(date[0..3].to_i, date[4..5].to_i, date[6..7].to_i)
|
145
|
+
when :RFCTYPE_BCD
|
146
|
+
size = metadata[:ucLength]
|
147
|
+
cb = FFI::MemoryPointer.new :char, size * 2
|
148
|
+
rc = NWRFCLib.get_chars(@handle, metadata[:name].cU, cb, size * 2, @error.to_ptr)
|
149
|
+
NWRFC.check_error(@error) if rc > 0
|
150
|
+
cb.read_string(size).uC
|
151
|
+
when :RFCTYPE_TIME
|
152
|
+
# TODO: See whether we can optimize this
|
153
|
+
timec = read_chars(metadata)
|
154
|
+
return Time.parse("#{timec[0..1]}:#{timec[2..3]}:#{timec[4..5]}")
|
155
|
+
when :RFCTYPE_BYTE
|
156
|
+
return read_chars(metadata)
|
157
|
+
when :RFCTYPE_TABLE
|
158
|
+
new_handle = NWRFCLib::RFCDataContainer.new
|
159
|
+
rc = NWRFCLib.get_table(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
|
160
|
+
NWRFC.check_error(@error) if rc > 0
|
161
|
+
# CAVEAT: Other calls using the handle require "handle" field
|
162
|
+
# of the RFC_DATA_CONTAINER struct for some reason.
|
163
|
+
new_handle = new_handle[:handle]
|
164
|
+
value = Table.new(new_handle)
|
165
|
+
when :RFCTYPE_NUM
|
166
|
+
return read_chars(metadata).to_i
|
167
|
+
when :RFCTYPE_FLOAT
|
168
|
+
double = FFI::MemoryPointer.new :double
|
169
|
+
rc = NWRFCLib.get_float(@handle, member.cU, double, @error)
|
170
|
+
NWRFC.check_error(@error) if rc > 0
|
171
|
+
return double.get_double(0)
|
172
|
+
when :RFCTYPE_INT
|
173
|
+
int = FFI::MemoryPointer.new :int
|
174
|
+
rc = NWRFCLib.get_int(@handle, member.cU, int, @error)
|
175
|
+
NWRFC.check_error(@error) if rc > 0
|
176
|
+
return int.get_int(0)
|
177
|
+
when :RFCTYPE_INT2
|
178
|
+
short = FFI::MemoryPointer.new :short
|
179
|
+
rc = NWRFCLib.get_int2(@handle, member.cU, short, @error)
|
180
|
+
NWRFC.check_error(@error) if rc > 0
|
181
|
+
return short.get_short(0)
|
182
|
+
when :RFCTYPE_INT1
|
183
|
+
int1 = FFI::MemoryPointer.new :uint8
|
184
|
+
rc = NWRFCLib.get_int1(@handle, member.cU, int1, @error)
|
185
|
+
NWRFC.check_error(@error) if rc > 0
|
186
|
+
return int1.get_uint8(0)
|
187
|
+
when :RFCTYPE_NULL
|
188
|
+
raise "Unsupported type RFCTYPE_NULL" #You should never run into this
|
189
|
+
when :RFCTYPE_STRUCTURE
|
190
|
+
new_handle = NWRFCLib::RFCDataContainer.new
|
191
|
+
rc = NWRFCLib.get_structure(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
|
192
|
+
NWRFC.check_error(@error) if rc > 0
|
193
|
+
new_handle = new_handle[:handle]
|
194
|
+
value = Structure.new(new_handle)
|
195
|
+
when :RFCTYPE_DECF16
|
196
|
+
return read_chars(metadata).to_f
|
197
|
+
when :RFCTYPE_DECF34
|
198
|
+
return read_chars(metadata).to_f
|
199
|
+
when :RFCTYPE_XMLDATA
|
200
|
+
raise "Unsupported type RFCTYPE_XMLDATA (no longer used)" #You should never run into this
|
201
|
+
when :RFCTYPE_STRING
|
202
|
+
return read_string(metadata)
|
203
|
+
when :RFCTYPE_XSTRING
|
204
|
+
else
|
205
|
+
raise "Illegal member type #{metadata[:type]}"
|
206
|
+
end
|
207
|
+
NWRFC.check_error(@error)
|
208
|
+
value
|
209
|
+
end
|
210
|
+
|
211
|
+
def []=(element, value)
|
212
|
+
member = element.to_s.upcase
|
213
|
+
metadata = member_metadata(element)
|
214
|
+
case metadata[:type]
|
215
|
+
when :RFCTYPE_CHAR
|
216
|
+
NWRFCLib.set_chars(@handle, member.cU, value.cU, value.length, @error.to_ptr)
|
217
|
+
when :RFCTYPE_DATE
|
218
|
+
value = value_to_date(value)
|
219
|
+
NWRFCLib.set_date(@handle, member.cU, value.cU, @error.to_ptr)
|
220
|
+
when :RFCTYPE_BCD
|
221
|
+
when :RFCTYPE_TIME
|
222
|
+
value = value_to_time(value)
|
223
|
+
NWRFCLib.set_time(@handle, member.cU, value.cU, @error.to_ptr)
|
224
|
+
when :RFCTYPE_BYTE
|
225
|
+
when :RFCTYPE_TABLE
|
226
|
+
when :RFCTYPE_NUM
|
227
|
+
when :RFCTYPE_FLOAT
|
228
|
+
NWRFCLib.set_float(@handle, member.cU, value.to_f, @error.to_ptr)
|
229
|
+
#NWRFCLib.set_chars(@handle, member.cU, value.to_s.cU, value.to_s.length, @error.to_ptr)
|
230
|
+
when :RFCTYPE_INT
|
231
|
+
NWRFCLib.set_int(@handle, member.cU, value.to_i, @error.to_ptr)
|
232
|
+
when :RFCTYPE_INT2
|
233
|
+
NWRFCLib.set_int2(@handle, member.cU, value.to_i, @error.to_ptr)
|
234
|
+
when :RFCTYPE_INT1
|
235
|
+
NWRFCLib.set_int1(@handle, member.cU, value.to_i, @error.to_ptr)
|
236
|
+
when :RFCTYPE_NULL
|
237
|
+
raise "Unsupported type RFCTYPE_NULL" #You should never run into this
|
238
|
+
when :RFCTYPE_STRUCTURE
|
239
|
+
when :RFCTYPE_DECF16
|
240
|
+
when :RFCTYPE_DECF34
|
241
|
+
when :RFCTYPE_XMLDATA
|
242
|
+
when :RFCTYPE_STRING
|
243
|
+
when :RFCTYPE_XSTRING
|
244
|
+
else
|
245
|
+
raise "Illegal member type #{@members[:type]}"
|
246
|
+
end
|
247
|
+
NWRFC.check_error(@error)
|
248
|
+
end
|
249
|
+
|
250
|
+
def value_to_time(value)
|
251
|
+
return value.strftime(NW_TIME_FORMAT) if value.respond_to? :strftime
|
252
|
+
value.to_s
|
253
|
+
end
|
254
|
+
|
255
|
+
def value_to_date(value)
|
256
|
+
return value.strftime(NW_DATE_FORMAT) if value.respond_to? :strftime
|
257
|
+
# Force the resulting string into 8 characters otherwise
|
258
|
+
value = value.to_s
|
259
|
+
value << ' ' until value.size == 8 if value.size < 8
|
260
|
+
value = value[0..7] if value.size > 8
|
261
|
+
value
|
262
|
+
end
|
263
|
+
|
264
|
+
def value_to_time(value)
|
265
|
+
return value.strftime(NW_TIME_FORMAT) if value.respond_to? :strftime
|
266
|
+
# Force the resulting string into 6 characters otherwise
|
267
|
+
value = value.to_s
|
268
|
+
value << ' ' until value.size == 6 if value.size < 6
|
269
|
+
value = value[0..6] if value.size > 6
|
270
|
+
value
|
271
|
+
end
|
272
|
+
|
273
|
+
# Get the metadata of a member (function, structure or table)
|
274
|
+
def member_metadata(member_name)
|
275
|
+
member = member_name.to_s.upcase
|
276
|
+
if self.class == NWRFC::FunctionCall
|
277
|
+
fpar = NWRFCLib::RFCFuncParam.new
|
278
|
+
rc = NWRFCLib.get_parameter_desc_by_name(@desc, member.cU, fpar.to_ptr, @error.to_ptr)
|
279
|
+
NWRFC.check_error(@error) if rc > 0
|
280
|
+
member_to_hash(fpar)
|
281
|
+
elsif self.class == NWRFC::Table || self.class == NWRFC::Structure
|
282
|
+
fd = NWRFCLib::RFCFieldDesc.new
|
283
|
+
rc = NWRFCLib.get_field_desc_by_name(@desc, member.cU, fd.to_ptr, @error.to_ptr)
|
284
|
+
NWRFC.check_error(@error) if rc > 0
|
285
|
+
member_to_hash(fd)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
private
|
290
|
+
# Returns the subset of metadata values common to both a function parameter
|
291
|
+
# and a type field
|
292
|
+
def member_to_hash(member)
|
293
|
+
{
|
294
|
+
:name => member[:name].get_str,
|
295
|
+
:type => NWRFCLib::RFCTYPE[member[:type]],
|
296
|
+
:nucLength => member[:nucLength],
|
297
|
+
:ucLength => member[:ucLength],
|
298
|
+
:decimals => member[:decimals],
|
299
|
+
:typeDescHandle => member[:typeDescHandle]
|
300
|
+
}
|
301
|
+
end
|
302
|
+
|
303
|
+
def read_chars(metadata)
|
304
|
+
size = metadata[:ucLength]
|
305
|
+
cb = FFI::MemoryPointer.new :char, size
|
306
|
+
rc = NWRFCLib.get_chars(@handle, metadata[:name].cU, cb, metadata[:nucLength], @error.to_ptr)
|
307
|
+
NWRFC.check_error(@error) if rc > 0
|
308
|
+
cb.read_string(size).uC
|
309
|
+
end
|
310
|
+
|
311
|
+
def read_string(metadata)
|
312
|
+
#size = metadata[:ucLength]
|
313
|
+
size = FFI::MemoryPointer.new(:uint)
|
314
|
+
rc = NWRFCLib.get_string_length(@handle, metadata[:name].cU, size, @error)
|
315
|
+
NWRFC.check_error(@error) if rc > 0
|
316
|
+
buf_len = size.read_uint + 1
|
317
|
+
sbuf = FFI::MemoryPointer.new :char, buf_len * NWRFCLib::B_SIZE
|
318
|
+
rc = NWRFCLib.get_string(@handle, metadata[:name].cU, sbuf, buf_len, size, @error)
|
319
|
+
NWRFC.check_error(@error) if rc > 0
|
320
|
+
sbuf.read_string(sbuf.size).uC
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|
324
|
+
|
325
|
+
class FunctionCall < DataContainer
|
326
|
+
attr_reader :handle, :desc, :connection, :function
|
327
|
+
|
328
|
+
def initialize(function)
|
329
|
+
@error = NWRFCLib::RFCError.new
|
330
|
+
@function = function
|
331
|
+
@connection = function.connection
|
332
|
+
@handle = NWRFCLib.create_function(@function.desc, @error.to_ptr)
|
333
|
+
@desc = function.desc
|
334
|
+
NWRFC.check_error(@error)
|
335
|
+
end
|
336
|
+
|
337
|
+
def invoke
|
338
|
+
rc = NWRFCLib.invoke(@connection.handle, @handle, @error.to_ptr)
|
339
|
+
NWRFC.check_error(@error) if rc > 0
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
|
344
|
+
class Table < DataContainer
|
345
|
+
|
346
|
+
include Enumerable
|
347
|
+
|
348
|
+
def each(&block)
|
349
|
+
rc = NWRFCLib.move_to_first_row(@handle, @error)
|
350
|
+
NWRFC.check_error(@error) if rc > 0
|
351
|
+
size.times do |row|
|
352
|
+
struct_handle = NWRFCLib.get_current_row(@handle, @error)
|
353
|
+
NWRFC.check_error(@error)
|
354
|
+
# CAVEAT: Other calls using the handle require "handle" field
|
355
|
+
# of the RFC_DATA_CONTAINER struct
|
356
|
+
yield Structure.new(struct_handle)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def size
|
361
|
+
rows = FFI::MemoryPointer.new(:uint)
|
362
|
+
rc = NWRFCLib.get_row_count(@handle, rows, @error)
|
363
|
+
rows.read_uint
|
364
|
+
end
|
365
|
+
|
366
|
+
# Delete all rows from (empty) the table
|
367
|
+
def clear
|
368
|
+
rc = delete_all_rows(@handle, @error)
|
369
|
+
NWRFC.check_error(@error) if rc > 0
|
370
|
+
end
|
371
|
+
|
372
|
+
# Retrieve the row at the given index
|
373
|
+
def [](index)
|
374
|
+
rc = NWRFCLib.move_to(@handle, index, @error)
|
375
|
+
NWRFC.check_error(@error) if rc > 0
|
376
|
+
struct_handle = NWRFCLib.get_current_row(@handle, @error)
|
377
|
+
NWRFC.check_error(@error)
|
378
|
+
Structure.new(struct_handle)
|
379
|
+
end
|
380
|
+
|
381
|
+
end #class Table
|
382
|
+
|
383
|
+
# Represents a structure. An instance is obtained internally by passing the
|
384
|
+
# handle of a structure. A user can obtain an instance by invoking sub-field
|
385
|
+
# access of a structure or a function
|
386
|
+
class Structure < DataContainer
|
387
|
+
|
388
|
+
# Return a list (array) of symbols representing the names of the fields
|
389
|
+
# of this structure
|
390
|
+
#---
|
391
|
+
# TODO: This is not working!
|
392
|
+
def fields
|
393
|
+
fc = FFI::MemoryPointer.new(:uint)
|
394
|
+
rc = NWRFCLib.get_field_count(@handle, fc, @error)
|
395
|
+
NWRFC.check_error(@error) if rc > 0
|
396
|
+
fc = fc.read_uint
|
397
|
+
fd = NWRFCLib::RFCFieldDesc.new
|
398
|
+
fields = []
|
399
|
+
debugger
|
400
|
+
fc.times do |index|
|
401
|
+
rc = NWRFCLib.get_field_desc_by_index(@handle, index, fd.to_ptr, @error.to_ptr)
|
402
|
+
NWRFC.check_error(@error) if rc > 0
|
403
|
+
fields << fd[:name].get_str.to_sym
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
end # class Structure
|
408
|
+
|
409
|
+
end #module NWRFC
|
@@ -0,0 +1,449 @@
|
|
1
|
+
# Author:: Martin Ceronio martin.ceronio@infosize.co.za
|
2
|
+
# Copyright:: Copyright (c) 2012 Martin Ceronio
|
3
|
+
# License:: MIT and/or Creative Commons Attribution-ShareAlike
|
4
|
+
# SAP, Netweaver, RFC and other names referred to in this code
|
5
|
+
# are, or may be registered trademarks and the property of SAP, AG
|
6
|
+
# No ownership over any of these is asserted by Martin Ceronio
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'ffi'
|
10
|
+
require 'iconv'
|
11
|
+
|
12
|
+
RUBY_VERSION_18 = RUBY_VERSION[0..2] == "1.8"
|
13
|
+
|
14
|
+
# Provide an alias for FFI::MemoryPointer#read_int to `read_uint` in 1.8
|
15
|
+
# See http://stackoverflow.com/questions/9035661/ruby-ffi-memorypointer-read-int-present-in-1-9-but-not-1-8
|
16
|
+
# Probably not good to interpret an unsigned int as an int, but we don't expect to use it for big values
|
17
|
+
# that could result in sign conversion
|
18
|
+
if RUBY_VERSION_18
|
19
|
+
FFI::MemoryPointer.class_eval{ alias :read_uint :read_int}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Enhancement to FFI::Pointer to be able to read a double-null terminated string,
|
23
|
+
# which would be returned e.g. by RfcGetVersion() in the SDK
|
24
|
+
# See http://stackoverflow.com/questions/9293307/ruby-ffi-ruby-1-8-reading-utf-16le-encoded-strings
|
25
|
+
module FFI
|
26
|
+
class Pointer
|
27
|
+
|
28
|
+
# Enhancement to FFI::Pointer to be able to read a double-null terminated string,
|
29
|
+
# which would be returned e.g. by RfcGetVersion() in the SDK
|
30
|
+
# See http://stackoverflow.com/questions/9293307/ruby-ffi-ruby-1-8-reading-utf-16le-encoded-strings
|
31
|
+
# It should be safe to call this on a Pointer within the context of the NW RFC SDK library,
|
32
|
+
# because all strings are supposed to be UTF-16LE encoded and double-null terminated
|
33
|
+
def read_string_dn(max=0)
|
34
|
+
cont_nullcount = 0
|
35
|
+
offset = 0
|
36
|
+
until cont_nullcount == 2
|
37
|
+
byte = get_bytes(offset,1)
|
38
|
+
cont_nullcount += 1 if byte == "\000"
|
39
|
+
cont_nullcount = 0 if byte != "\000"
|
40
|
+
offset += 1
|
41
|
+
end
|
42
|
+
get_bytes(0,offset+1)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Enhancement to the String class to put string values into double-null
|
48
|
+
# terminated UTF16 little endian encoded strings as required by the NW RFC
|
49
|
+
# SDK function, which should work on Linux and Windows (and maybe other
|
50
|
+
# architectures, though the plan is not to support them)
|
51
|
+
#String.class_eval{define_method(:cU){ Iconv.conv("UTF-16LE", "UTF8", self+"\0") }}
|
52
|
+
class String
|
53
|
+
def cU
|
54
|
+
NWRFCLib::Cutf8_to_utf16le.iconv(self+"\0")
|
55
|
+
end
|
56
|
+
|
57
|
+
def uC
|
58
|
+
NWRFCLib::Cutf16le_to_utf8.iconv(self).strip
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
#String.class_eval{define_method(:cU){ NWRFCLib::Cutf8_to_utf16le.iconv(self+"\0") }}
|
63
|
+
|
64
|
+
# Enhancement to FFI::StructLayout::CharArray to add a get_str method that changes the
|
65
|
+
# string value of the character array by enforcing encoding of UTF-16LE (as used in NW RFC SDK)
|
66
|
+
# and strips off blanks at the end to return a readable String
|
67
|
+
class FFI::StructLayout::CharArray
|
68
|
+
def get_str
|
69
|
+
#Iconv.conv("UTF8", "UTF-16LE", self.to_ptr.read_string(self.size)).strip
|
70
|
+
NWRFCLib::Cutf16le_to_utf8.iconv(self.to_ptr.read_string(self.size)).strip
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Library wrapper around NW RFC SDK shared library using RUBY-FFI
|
75
|
+
# FIXME: Make structs managed structs and deconstruct them on GC
|
76
|
+
module NWRFCLib
|
77
|
+
|
78
|
+
Cutf8_to_utf16le = Iconv.new("UTF-16LE", "UTF8")
|
79
|
+
Cutf16le_to_utf8 = Iconv.new("UTF8", "UTF-16LE")
|
80
|
+
|
81
|
+
extend FFI::Library
|
82
|
+
ffi_lib '/home/martin/nwrfcsdk/lib/libsapnwrfc.so'
|
83
|
+
|
84
|
+
# Multiplier for providing correct byte size for String passed to RFC library
|
85
|
+
#TODO: Make platform-dependent size based on RUBY_PLATFORM
|
86
|
+
B_SIZE = 2
|
87
|
+
|
88
|
+
RFC_RC = enum(
|
89
|
+
:RFC_OK,
|
90
|
+
:RFC_COMMUNICATION_FAILURE,
|
91
|
+
:RFC_LOGON_FAILURE,
|
92
|
+
:RFC_ABAP_RUNTIME_FAILURE,
|
93
|
+
:RFC_ABAP_MESSAGE,
|
94
|
+
:RFC_ABAP_EXCEPTION,
|
95
|
+
:RFC_CLOSED,
|
96
|
+
:RFC_CANCELED,
|
97
|
+
:RFC_TIMEOUT,
|
98
|
+
:RFC_MEMORY_INSUFFICIENT,
|
99
|
+
:RFC_VERSION_MISMATCH,
|
100
|
+
:RFC_INVALID_PROTOCOL,
|
101
|
+
:RFC_SERIALIZATION_FAILURE,
|
102
|
+
:RFC_INVALID_HANDLE,
|
103
|
+
:RFC_RETRY,
|
104
|
+
:RFC_EXTERNAL_FAILURE,
|
105
|
+
:RFC_EXECUTED,
|
106
|
+
:RFC_NOT_FOUND,
|
107
|
+
:RFC_NOT_SUPPORTED,
|
108
|
+
:RFC_ILLEGAL_STATE,
|
109
|
+
:RFC_INVALID_PARAMETER,
|
110
|
+
:RFC_CODEPAGE_CONVERSION_FAILURE,
|
111
|
+
:RFC_CONVERSION_FAILURE,
|
112
|
+
:RFC_BUFFER_TOO_SMALL,
|
113
|
+
:RFC_TABLE_MOVE_BOF,
|
114
|
+
:RFC_TABLE_MOVE_EOF,
|
115
|
+
:RFC_UNKNOWN_ERROR
|
116
|
+
)
|
117
|
+
|
118
|
+
RFC_ERROR_GROUP = enum(
|
119
|
+
:OK,
|
120
|
+
:ABAP_APPLICATION_FAILURE,
|
121
|
+
:ABAP_RUNTIME_FAILURE,
|
122
|
+
:LOGON_FAILURE,
|
123
|
+
:COMMUNICATION_FAILURE,
|
124
|
+
:EXTERNAL_RUNTIME_FAILURE,
|
125
|
+
:EXTERNAL_APPLICATION_FAILURE
|
126
|
+
)
|
127
|
+
|
128
|
+
RFC_DIRECTION = enum(
|
129
|
+
:RFC_IMPORT, 1,
|
130
|
+
:RFC_EXPORT, 2,
|
131
|
+
:RFC_CHANGING, 3,
|
132
|
+
:RFC_TABLES, 7
|
133
|
+
)
|
134
|
+
|
135
|
+
RFCTYPE = enum(
|
136
|
+
:RFCTYPE_CHAR , 0,
|
137
|
+
:RFCTYPE_DATE , 1,
|
138
|
+
:RFCTYPE_BCD , 2,
|
139
|
+
:RFCTYPE_TIME , 3,
|
140
|
+
:RFCTYPE_BYTE , 4,
|
141
|
+
:RFCTYPE_TABLE , 5,
|
142
|
+
:RFCTYPE_NUM , 6,
|
143
|
+
:RFCTYPE_FLOAT , 7,
|
144
|
+
:RFCTYPE_INT , 8,
|
145
|
+
:RFCTYPE_INT2 , 9,
|
146
|
+
:RFCTYPE_INT1 , 10,
|
147
|
+
:RFCTYPE_NULL , 14,
|
148
|
+
:RFCTYPE_STRUCTURE , 17,
|
149
|
+
:RFCTYPE_DECF16 , 23,
|
150
|
+
:RFCTYPE_DECF34 , 24,
|
151
|
+
:RFCTYPE_XMLDATA , 28,
|
152
|
+
:RFCTYPE_STRING , 29,
|
153
|
+
:RFCTYPE_XSTRING , 30
|
154
|
+
)
|
155
|
+
|
156
|
+
# Connection parameter wrapper (struct RFC_CONNECTION_PARAMETER in sapnwrfc.h)
|
157
|
+
class RFCConnParam < FFI::Struct
|
158
|
+
layout :name, :pointer,
|
159
|
+
:value, :pointer
|
160
|
+
end
|
161
|
+
|
162
|
+
# Connection Details (struct RFC_ATTRIBUTES in sapnwrfc.h)
|
163
|
+
class RFCConnection < FFI::Struct
|
164
|
+
layout :dest, [:char, (64+1)*B_SIZE],
|
165
|
+
:host, [:char, (100+1)*B_SIZE],
|
166
|
+
:partnerHost, [:char, (100+1)*B_SIZE],
|
167
|
+
:sysNumber, [:char, (2+1)*B_SIZE],
|
168
|
+
:sysId, [:char, (8+1)*B_SIZE],
|
169
|
+
:client, [:char, (3+1)*B_SIZE],
|
170
|
+
:user, [:char, (12+1)*B_SIZE],
|
171
|
+
:language, [:char, (2+1)*B_SIZE],
|
172
|
+
:trace, [:char, (1+1)*B_SIZE],
|
173
|
+
:isoLanguage, [:char, (2+1)*B_SIZE],
|
174
|
+
:codepage, [:char, (4+1)*B_SIZE],
|
175
|
+
:partnerCodepage, [:char, (4+1)*B_SIZE],
|
176
|
+
:rfcRole, [:char, (1+1)*B_SIZE],
|
177
|
+
:type, [:char, (1+1)*B_SIZE],
|
178
|
+
:partnerType, [:char, (1+1)*B_SIZE],
|
179
|
+
:rel, [:char, (4+1)*B_SIZE],
|
180
|
+
:partnerRel, [:char, (4+1)*B_SIZE],
|
181
|
+
:kernelRel, [:char, (4+1)*B_SIZE],
|
182
|
+
:cpicConvId, [:char, (8+1)*B_SIZE],
|
183
|
+
:progName, [:char, (128+1)*B_SIZE],
|
184
|
+
:reserved, [:char, (86+1)*B_SIZE]
|
185
|
+
end
|
186
|
+
|
187
|
+
# Error info wrapper (struct RFC_ERROR_INFO in sapnwrfc.h)
|
188
|
+
class RFCError < FFI::Struct
|
189
|
+
layout :code, :int,
|
190
|
+
:group, :int,
|
191
|
+
:key, [:char, (128)*B_SIZE],
|
192
|
+
:message, [:char, (512)*B_SIZE],
|
193
|
+
:abapMsgClass, [:char, (20+1)*B_SIZE],
|
194
|
+
:abapMsgType, [:char, (1+1)*B_SIZE],
|
195
|
+
:abapMsgNumber, [:char, (3+1)*B_SIZE],
|
196
|
+
:abapMsgV1, [:char, (50+1)*B_SIZE],
|
197
|
+
:abapMsgV2, [:char, (50+1)*B_SIZE],
|
198
|
+
:abapMsgV3, [:char, (50+1)*B_SIZE],
|
199
|
+
:abapMsgV4, [:char, (50+1)*B_SIZE]
|
200
|
+
end
|
201
|
+
|
202
|
+
# Function Parameter Description (struct RFC_PARAMETER_DESC in sapnwrfc.h)
|
203
|
+
class RFCFuncParam < FFI::Struct
|
204
|
+
layout :name, [:char, (30+1)*B_SIZE],
|
205
|
+
:type, :int, #enum RFCTYPE
|
206
|
+
:direction, :int, #enum RFC_DIRECTION
|
207
|
+
:nucLength, :uint,
|
208
|
+
:ucLength, :uint,
|
209
|
+
:decimals, :uint,
|
210
|
+
:typeDescHandle, :pointer, #RFC_TYPE_DESC_HANDLE
|
211
|
+
:defaultValue, [:char, (30+1)*B_SIZE], #RFC_PARAMETER_DEFVALUE
|
212
|
+
:parameterText, [:char, (79+1)*B_SIZE], #RFC_PARAMETER_TEXT
|
213
|
+
:optional, :uchar, #RFC_BYTE
|
214
|
+
:extendedDescription, :pointer
|
215
|
+
end
|
216
|
+
|
217
|
+
class RFCFieldDesc < FFI::Struct
|
218
|
+
layout :name, [:char, (30+1)*B_SIZE],
|
219
|
+
:type, :int, #enum RFCTYPE
|
220
|
+
:nucLength, :uint,
|
221
|
+
:nucOffset, :uint,
|
222
|
+
:ucLength, :uint,
|
223
|
+
:ucOffset, :uint,
|
224
|
+
:decimals, :uint,
|
225
|
+
:typeDescHandle, :pointer, #RFC_TYPE_DESC_HANDLE
|
226
|
+
:extendedDescription, :pointer
|
227
|
+
end
|
228
|
+
|
229
|
+
class RFCDataContainer < FFI::Struct
|
230
|
+
layout :handle, :pointer
|
231
|
+
end
|
232
|
+
|
233
|
+
# typedef :RFCDataContainer, RFCStructureHandle
|
234
|
+
# typedef :RFCDataContainer, RFCTableHandle
|
235
|
+
|
236
|
+
class RFC_FUNCTION_DESC_HANDLE < FFI::Struct
|
237
|
+
layout :handle, :pointer
|
238
|
+
end
|
239
|
+
|
240
|
+
class RFC_TYPE_DESC_HANDLE < FFI::Struct
|
241
|
+
layout :handle, :pointer
|
242
|
+
end
|
243
|
+
|
244
|
+
class DATA_CONTAINER_HANDLE < FFI::Struct
|
245
|
+
layout :handle, :pointer
|
246
|
+
end
|
247
|
+
|
248
|
+
#---
|
249
|
+
# attach_function :init, :RfcInit, [], :void
|
250
|
+
# # Connection handling:
|
251
|
+
# attach_function :open_connection, :RfcOpenConnection,
|
252
|
+
# [:pointer, :uint, :pointer], :pointer
|
253
|
+
# attach_function :close_connection, :RfcCloseConnection,
|
254
|
+
# [:pointer, :pointer], :void #(handle, error)
|
255
|
+
# attach_function :get_connection_attributes, :RfcGetConnectionAttributes,
|
256
|
+
# [:pointer, :pointer, :pointer], :void #(conn_handle, conninfo, error)
|
257
|
+
# # Function Handling & Calling
|
258
|
+
# attach_function :get_function_desc, :RfcGetFunctionDesc,
|
259
|
+
# [:pointer, :pointer, :pointer], :pointer #(conn_handle, func_name, error)
|
260
|
+
# attach_function :create_function, :RfcCreateFunction,
|
261
|
+
# [:pointer, :pointer], :pointer #(fd_handle, error), :func_handle
|
262
|
+
# attach_function :invoke, :RfcInvoke,
|
263
|
+
# [:pointer, :pointer, :pointer], :int #(conn_handle, func_handle, error), RC
|
264
|
+
# #TODO: Do we want to include RfcDestroyFunction for releasing its memory?
|
265
|
+
# attach_function :get_param_count, :RfcGetParameterCount,
|
266
|
+
# [:pointer, :pointer, :pointer], :void #(fd_handle, uint, error)
|
267
|
+
# attach_function :get_parameter_desc_by_index, :RfcGetParameterDescByIndex,
|
268
|
+
# [:pointer, :uint, :pointer, :pointer], :void #(fd_handle, idx, param, error)
|
269
|
+
# # Field Handling
|
270
|
+
# # RFC_RC RfcGetChars(handle, name, buffer, length, error)
|
271
|
+
# attach_function :get_chars, :RfcGetChars,
|
272
|
+
# [:pointer, :pointer, :pointer, :uint, :pointer], :RFC_RC
|
273
|
+
# # RFC_RC RfcGetChars(handle, name, buffer, length, error)
|
274
|
+
# attach_function :get_num, :RfcGetNum,
|
275
|
+
# [:pointer, :pointer, :pointer, :uint, :pointer], :RFC_RC
|
276
|
+
# # RFC_RC RfcGetDate
|
277
|
+
# attach_function :get_date, :RfcGetDate,
|
278
|
+
# [:pointer, :pointer, [:char, 8], :pointer], :RFC_RC
|
279
|
+
# # RFC_RC RfcGetDate
|
280
|
+
# attach_function :get_time, :RfcGetTime,
|
281
|
+
# [:pointer, :pointer, [:char, 6], :pointer], :RFC_RC
|
282
|
+
# # RFC_RC RfcGetString
|
283
|
+
# # attach_function :get_string, :RfcGetString,
|
284
|
+
# # [:pointer, :pointer, :pointer], :RFC_RC
|
285
|
+
# # RFC RfcGetBytes
|
286
|
+
# attach_function :get_bytes, :RfcGetBytes,
|
287
|
+
# [:pointer, :pointer, :pointer, :uint, :pointer], :RFC_RC
|
288
|
+
# # RFC_RC RfcGetXString
|
289
|
+
# attach_function :get_xstring, :RfcGetXString,
|
290
|
+
# [:pointer, :pointer, :pointer, :uint, :pointer, :pointer], :RFC_RC
|
291
|
+
# # RFC_RC RfcGetStringLength(dataHandle, name, *length, error)
|
292
|
+
# attach_function :get_string_length, :RfcGetStringLength,
|
293
|
+
# [:pointer, :pointer, :pointer, :pointer], :int
|
294
|
+
# # void RfcSetChars(dataHandle, name, value, length, error)
|
295
|
+
# attach_function :set_chars, :RfcSetChars,
|
296
|
+
# [:pointer, :pointer, :pointer, :uint, :pointer], :void
|
297
|
+
#+++
|
298
|
+
|
299
|
+
#############################################################################################################
|
300
|
+
# ATTACH FUNCTIONS
|
301
|
+
#############################################################################################################
|
302
|
+
[
|
303
|
+
[:add_exception, :RfcAddException, [:pointer, :pointer, :pointer], :int],
|
304
|
+
[:add_function_desc, :RfcAddFunctionDesc, [:pointer, :pointer, :pointer], :int],
|
305
|
+
[:add_parameter, :RfcAddParameter, [:pointer, :pointer, :pointer], :int],
|
306
|
+
[:add_type_desc, :RfcAddTypeDesc, [:pointer, :pointer, :pointer], :int],
|
307
|
+
[:add_type_field, :RfcAddTypeField, [:pointer, :pointer, :pointer], :int],
|
308
|
+
[:append_new_row, :RfcAppendNewRow, [:pointer, :pointer], :pointer],
|
309
|
+
[:append_row, :RfcAppendRow, [:pointer, :pointer, :pointer], :int],
|
310
|
+
[:clone_structure, :RfcCloneStructure, [:pointer, :pointer], :pointer],
|
311
|
+
[:clone_table, :RfcCloneTable, [:pointer, :pointer], :pointer],
|
312
|
+
[:close_connection, :RfcCloseConnection, [:pointer, :pointer], :int],
|
313
|
+
[:confirm_transaction, :RfcConfirmTransaction, [:pointer, :pointer], :int],
|
314
|
+
[:create_function, :RfcCreateFunction, [:pointer, :pointer], :pointer],
|
315
|
+
[:create_function_desc, :RfcCreateFunctionDesc, [:pointer, :pointer], :pointer],
|
316
|
+
[:create_structure, :RfcCreateStructure, [:pointer, :pointer], :pointer],
|
317
|
+
[:create_table, :RfcCreateTable, [:pointer, :pointer], :pointer],
|
318
|
+
[:create_transaction, :RfcCreateTransaction, [:pointer, :pointer, :pointer, :pointer], :pointer],
|
319
|
+
[:create_type_desc, :RfcCreateTypeDesc, [:pointer, :pointer], :pointer],
|
320
|
+
[:delete_all_rows, :RfcDeleteAllRows, [:pointer, :pointer], :int],
|
321
|
+
[:delete_current_row, :RfcDeleteCurrentRow, [:pointer, :pointer], :int],
|
322
|
+
[:describe_function, :RfcDescribeFunction, [:pointer, :pointer], :pointer],
|
323
|
+
[:describe_type, :RfcDescribeType, [:pointer, :pointer], :pointer],
|
324
|
+
[:destroy_function, :RfcDestroyFunction, [:pointer, :pointer], :int],
|
325
|
+
[:destroy_function_desc, :RfcDestroyFunctionDesc, [:pointer, :pointer], :int],
|
326
|
+
[:destroy_structure, :RfcDestroyStructure, [:pointer, :pointer], :int],
|
327
|
+
[:destroy_table, :RfcDestroyTable, [:pointer, :pointer], :int],
|
328
|
+
[:destroy_transaction, :RfcDestroyTransaction, [:pointer, :pointer], :int],
|
329
|
+
[:destroy_type_desc, :RfcDestroyTypeDesc, [:pointer, :pointer], :int],
|
330
|
+
[:enable_basxml, :RfcEnableBASXML, [:pointer, :pointer], :int],
|
331
|
+
[:get_bytes, :RfcGetBytes, [:pointer, :pointer, :pointer, :uint, :pointer], :int],
|
332
|
+
[:get_cached_function_desc, :RfcGetCachedFunctionDesc, [:pointer, :pointer, :pointer], :pointer],
|
333
|
+
[:get_cached_type_desc, :RfcGetCachedTypeDesc, [:pointer, :pointer, :pointer], :pointer],
|
334
|
+
[:get_chars, :RfcGetChars, [:pointer, :pointer, :pointer, :uint, :pointer], :int],
|
335
|
+
[:get_connection_attributes, :RfcGetConnectionAttributes, [:pointer, :pointer, :pointer], :int],
|
336
|
+
[:get_current_row, :RfcGetCurrentRow, [:pointer, :pointer], :pointer],
|
337
|
+
[:get_date, :RfcGetDate, [:pointer, :pointer, :pointer, :pointer], :int],
|
338
|
+
[:get_dec_f16, :RfcGetDecF16, [:pointer, :pointer, :pointer, :pointer], :int],
|
339
|
+
[:get_dec_f34, :RfcGetDecF34, [:pointer, :pointer, :pointer, :pointer], :int],
|
340
|
+
[:get_direction_as_string, :RfcGetDirectionAsString, [:pointer], :pointer],
|
341
|
+
[:get_exception_count, :RfcGetExceptionCount, [:pointer, :pointer, :pointer], :int],
|
342
|
+
[:get_exception_desc_by_index, :RfcGetExceptionDescByIndex, [:pointer, :uint, :pointer, :pointer], :int],
|
343
|
+
[:get_exception_desc_by_name, :RfcGetExceptionDescByName, [:pointer, :pointer, :pointer, :pointer], :int],
|
344
|
+
[:get_field_count, :RfcGetFieldCount, [:pointer, :pointer, :pointer], :int],
|
345
|
+
[:get_field_desc_by_index, :RfcGetFieldDescByIndex, [:pointer, :uint, :pointer, :pointer], :int],
|
346
|
+
[:get_field_desc_by_name, :RfcGetFieldDescByName, [:pointer, :pointer, :pointer, :pointer], :int],
|
347
|
+
[:get_float, :RfcGetFloat, [:pointer, :pointer, :pointer, :pointer], :int],
|
348
|
+
[:get_function_desc, :RfcGetFunctionDesc, [:pointer, :pointer, :pointer], :pointer],
|
349
|
+
[:get_function_name, :RfcGetFunctionName, [:pointer, :pointer, :pointer], :int],
|
350
|
+
[:get_int, :RfcGetInt, [:pointer, :pointer, :pointer, :pointer], :int],
|
351
|
+
[:get_int1, :RfcGetInt1, [:pointer, :pointer, :pointer, :pointer], :int],
|
352
|
+
[:get_int2, :RfcGetInt2, [:pointer, :pointer, :pointer, :pointer], :int],
|
353
|
+
[:get_num, :RfcGetNum, [:pointer, :pointer, :pointer, :uint, :pointer], :int],
|
354
|
+
[:get_parameter_count, :RfcGetParameterCount, [:pointer, :pointer, :pointer], :int],
|
355
|
+
[:get_parameter_desc_by_index, :RfcGetParameterDescByIndex, [:pointer, :uint, :pointer, :pointer], :int],
|
356
|
+
[:get_parameter_desc_by_name, :RfcGetParameterDescByName, [:pointer, :pointer, :pointer, :pointer], :int],
|
357
|
+
[:get_partner_snc_key, :RfcGetPartnerSNCKey, [:pointer, :pointer, :pointer, :pointer], :int],
|
358
|
+
[:get_partner_snc_name, :RfcGetPartnerSNCName, [:pointer, :pointer, :uint, :pointer], :int],
|
359
|
+
[:get_partner_sso_ticket, :RfcGetPartnerSSOTicket, [:pointer, :pointer, :pointer, :pointer], :int],
|
360
|
+
[:get_rc_as_string, :RfcGetRcAsString, [:pointer], :pointer],
|
361
|
+
[:get_row_count, :RfcGetRowCount, [:pointer, :pointer, :pointer], :int],
|
362
|
+
[:get_string, :RfcGetString, [:pointer, :pointer, :pointer, :uint, :pointer, :pointer], :int],
|
363
|
+
[:get_string_length, :RfcGetStringLength, [:pointer, :pointer, :pointer, :pointer], :int],
|
364
|
+
[:get_structure, :RfcGetStructure, [:pointer, :pointer, :pointer, :pointer], :int],
|
365
|
+
[:get_table, :RfcGetTable, [:pointer, :pointer, :pointer, :pointer], :int],
|
366
|
+
[:get_time, :RfcGetTime, [:pointer, :pointer, :pointer, :pointer], :int],
|
367
|
+
[:get_transaction_id, :RfcGetTransactionID, [:pointer, :pointer, :pointer], :int],
|
368
|
+
[:get_type_as_string, :RfcGetTypeAsString, [:pointer], :pointer],
|
369
|
+
[:get_type_desc, :RfcGetTypeDesc, [:pointer, :pointer, :pointer], :pointer],
|
370
|
+
[:get_type_length, :RfcGetTypeLength, [:pointer, :pointer, :pointer, :pointer], :int],
|
371
|
+
[:get_type_name, :RfcGetTypeName, [:pointer, :pointer, :pointer], :int],
|
372
|
+
[:get_version, :RfcGetVersion, [:pointer, :pointer, :pointer], :pointer],
|
373
|
+
[:get_x_string, :RfcGetXString, [:pointer, :pointer, :pointer, :uint, :pointer, :pointer], :int],
|
374
|
+
[:init, :RfcInit, [:pointer], :int],
|
375
|
+
[:insert_new_row, :RfcInsertNewRow, [:pointer, :pointer], :pointer],
|
376
|
+
[:insert_row, :RfcInsertRow, [:pointer, :pointer, :pointer], :int],
|
377
|
+
[:install_generic_server_function, :RfcInstallGenericServerFunction, [:pointer, :pointer, :pointer], :int],
|
378
|
+
[:install_server_function, :RfcInstallServerFunction, [:pointer, :pointer, :pointer, :pointer], :int],
|
379
|
+
[:install_transaction_handlers, :RfcInstallTransactionHandlers, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int],
|
380
|
+
[:invoke, :RfcInvoke, [:pointer, :pointer, :pointer], :int],
|
381
|
+
[:invoke_in_transaction, :RfcInvokeInTransaction, [:pointer, :pointer, :pointer], :int],
|
382
|
+
[:is_basxml_supported, :RfcIsBASXMLSupported, [:pointer, :pointer, :pointer], :int],
|
383
|
+
#[:is_connection_handle_valid, :RfcIsConnectionHandleValid, [:pointer, :pointer, :pointer], :int],
|
384
|
+
[:is_parameter_active, :RfcIsParameterActive, [:pointer, :pointer, :pointer, :pointer], :int],
|
385
|
+
[:listen_and_dispatch, :RfcListenAndDispatch, [:pointer, :int, :pointer], :int],
|
386
|
+
[:move_to, :RfcMoveTo, [:pointer, :uint, :pointer], :int],
|
387
|
+
[:move_to_first_row, :RfcMoveToFirstRow, [:pointer, :pointer], :int],
|
388
|
+
[:move_to_last_row, :RfcMoveToLastRow, [:pointer, :pointer], :int],
|
389
|
+
[:move_to_next_row, :RfcMoveToNextRow, [:pointer, :pointer], :int],
|
390
|
+
[:move_to_previous_row, :RfcMoveToPreviousRow, [:pointer, :pointer], :int],
|
391
|
+
[:open_connection, :RfcOpenConnection, [:pointer, :uint, :pointer], :pointer],
|
392
|
+
[:ping, :RfcPing, [:pointer, :pointer], :int],
|
393
|
+
[:register_server, :RfcRegisterServer, [:pointer, :uint, :pointer], :pointer],
|
394
|
+
[:reload_ini_file, :RfcReloadIniFile, [:pointer], :int],
|
395
|
+
# [:remove_function_desc, :RfcRemoveFunctionDesc, [:pointer, :pointer, :pointer], :int],
|
396
|
+
# [:remove_type_desc, :RfcRemoveTypeDesc, [:pointer, :pointer, :pointer], :int],
|
397
|
+
[:reset_server_context, :RfcResetServerContext, [:pointer, :pointer], :int],
|
398
|
+
[:sapuc_to_utf8, :RfcSAPUCToUTF8, [:pointer, :uint, :pointer, :pointer, :pointer, :pointer], :int],
|
399
|
+
[:set_bytes, :RfcSetBytes, [:pointer, :pointer, :pointer, :uint, :pointer], :int],
|
400
|
+
[:set_chars, :RfcSetChars, [:pointer, :pointer, :pointer, :uint, :pointer], :int],
|
401
|
+
[:set_date, :RfcSetDate, [:pointer, :pointer, :pointer, :pointer], :int],
|
402
|
+
[:set_dec_f16, :RfcSetDecF16, [:pointer, :pointer, :pointer, :pointer], :int],
|
403
|
+
[:set_dec_f34, :RfcSetDecF34, [:pointer, :pointer, :pointer, :pointer], :int],
|
404
|
+
[:set_float, :RfcSetFloat, [:pointer, :pointer, :double, :pointer], :int],
|
405
|
+
[:set_ini_path, :RfcSetIniPath, [:pointer, :pointer], :int],
|
406
|
+
[:set_int, :RfcSetInt, [:pointer, :pointer, :long, :pointer], :int],
|
407
|
+
[:set_int1, :RfcSetInt1, [:pointer, :pointer, :uint8, :pointer], :int],
|
408
|
+
[:set_int2, :RfcSetInt2, [:pointer, :pointer, :short, :pointer], :int],
|
409
|
+
[:set_num, :RfcSetNum, [:pointer, :pointer, :pointer, :uint, :pointer], :int],
|
410
|
+
[:set_parameter_active, :RfcSetParameterActive, [:pointer, :pointer, :int, :pointer], :int],
|
411
|
+
[:set_string, :RfcSetString, [:pointer, :pointer, :pointer, :uint, :pointer], :int],
|
412
|
+
[:set_structure, :RfcSetStructure, [:pointer, :pointer, :pointer, :pointer], :int],
|
413
|
+
[:set_table, :RfcSetTable, [:pointer, :pointer, :pointer, :pointer], :int],
|
414
|
+
[:set_time, :RfcSetTime, [:pointer, :pointer, :pointer, :pointer], :int],
|
415
|
+
#[:set_trace_dir, :RfcSetTraceDir, [:pointer, :pointer], :int],
|
416
|
+
#[:set_trace_encoding, :RfcSetTraceEncoding, [:pointer, :pointer], :int],
|
417
|
+
#[:set_trace_level, :RfcSetTraceLevel, [:pointer, :pointer, :uint, :pointer], :int],
|
418
|
+
[:set_type_length, :RfcSetTypeLength, [:pointer, :uint, :uint, :pointer], :int],
|
419
|
+
[:set_x_string, :RfcSetXString, [:pointer, :pointer, :pointer, :uint, :pointer], :int],
|
420
|
+
[:snc_key_to_name, :RfcSNCKeyToName, [:pointer, :pointer, :uint, :pointer, :uint, :pointer], :int],
|
421
|
+
[:snc_name_to_key, :RfcSNCNameToKey, [:pointer, :pointer, :pointer, :pointer, :pointer], :int],
|
422
|
+
[:start_server, :RfcStartServer, [:int, :pointer, :pointer, :uint, :pointer], :pointer],
|
423
|
+
[:submit_transaction, :RfcSubmitTransaction, [:pointer, :pointer], :int],
|
424
|
+
[:utf8_to_sapuc, :RfcUTF8ToSAPUC, [:pointer, :uint, :pointer, :pointer, :pointer, :pointer], :int]
|
425
|
+
].each{|funcsig|
|
426
|
+
# puts funcsig.to_s
|
427
|
+
attach_function(funcsig[0], funcsig[1], funcsig[2], funcsig[3])
|
428
|
+
}
|
429
|
+
|
430
|
+
# Take Hash of connection parameters and returns FFI pointer to an array
|
431
|
+
# for passing to connection
|
432
|
+
def NWRFCLib.make_conn_params(params) #https://github.com/ffi/ffi/wiki/Structs
|
433
|
+
par = FFI::MemoryPointer.new(RFCConnParam, params.length)
|
434
|
+
pars = params.length.times.collect do |i|
|
435
|
+
RFCConnParam.new(par + i * RFCConnParam.size)
|
436
|
+
end
|
437
|
+
#TODO Optimize this method
|
438
|
+
tpar = params.to_a
|
439
|
+
params.length.times do |n|
|
440
|
+
# str = (tpar[n][0].to_s + "\0").encode("UTF-16LE")
|
441
|
+
pars[n][:name] = FFI::MemoryPointer.from_string(tpar[n][0].to_s.cU)
|
442
|
+
# str = (tpar[n][1].to_s + "\0").encode("UTF-16LE")
|
443
|
+
# str = str.encode("UTF-16LE")
|
444
|
+
pars[n][:value] = FFI::MemoryPointer.from_string(tpar[n][1].to_s.cU)
|
445
|
+
end
|
446
|
+
par
|
447
|
+
end
|
448
|
+
|
449
|
+
end
|