How to use https://github.com/ZigRFC/ZigRFC - client example.
Download .lib and .dll files from https://support.sap.com/en/product/connectors/nwrfcsdk.html into folder src/dll/[here]
Create remote function module on your system:
FUNCTION ZIG_RFC_DECOMPRESS
IMPORTING
VALUE(COMPRESSED) TYPE XSTRING
EXPORTING
VALUE(DECOMPRESSED) TYPE STRING.
ENDFUNCTION.Create connection in SM59 (type T, program ID same as used in parameters).
Allow server to register itself at SAP gateway:
- Transaction SMGW -> Goto -> Expert Functions -> External Security -> Maintenance of ACL files -> RegInfo File
- Parameters: P/D=P and TP=ZIG_RFC_SERVER, rest can be * for testing but should be filled for security with correct data
The following wil allow the function ZIG_RFC_DECOMPRESS to be called form ABAP at destination specified in SM59.
const std = @import("std");
const rfc = @import("rfc");
const W = std.unicode.utf8ToUtf16LeStringLiteral;
fn createFuncDesc() !*rfc.desc.FunctionDesc {
var error_info: rfc.ErrorInfo = undefined;
const descr = rfc.desc.RfcCreateFunctionDesc(W("ZIG_RFC_DECOMPRESS"), null);
var parameters: [2]rfc.desc.ParameterDesc = .{
.{ .type = .xstring, .direction = .import },
.{ .type = .string, .direction = .@"export" },
};
@memcpy(parameters[0].name[0..10], W("COMPRESSED"));
@memcpy(parameters[1].name[0..12], W("DECOMPRESSED"));
for (¶meters) |*p| {
if (descr.AddParameter(p, &error_info) != .ok) {
try rfc.PrintErrorToLog(&error_info);
return error.CouldNotAddParameter;
}
}
return descr;
}
fn ZIG_RFC_DECOMPRESS(rfc_handle: *rfc.connection.Connection, func_handle: *rfc.data_container.Function, error_info: *rfc.ErrorInfo) callconv(.c) rfc.ReturnCode {
_ = rfc_handle;
std.log.info("ZIG_RFC_DECOMPRESS called...", .{});
var dummy_buffer: [0]u8 = undefined;
var xstring_len: c_uint = undefined;
switch (func_handle.GetXString(W("COMPRESSED"), &dummy_buffer, @intCast(dummy_buffer.len), &xstring_len, error_info)) {
.buffer_too_small => {
std.log.info("COMPRESSED required len={d}...", .{xstring_len});
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
//xstring_len has required length
var xstring_buffer = allocator.alloc(u8, xstring_len) catch unreachable;
defer allocator.free(xstring_buffer);
if (func_handle.GetXString(W("COMPRESSED"), xstring_buffer.ptr, @intCast(xstring_buffer.len), &xstring_len, error_info) != .ok) {
rfc.PrintErrorToLog(error_info) catch unreachable;
return .invalid_parameter;
}
var reader = std.Io.Reader.fixed(xstring_buffer);
var flate_buffer: [std.compress.flate.max_window_len]u8 = undefined;
var flate = std.compress.flate.Decompress.init(&reader, std.compress.flate.Container.zlib, &flate_buffer);
var string_buffer = flate.reader.allocRemaining(allocator, .unlimited) catch unreachable;
defer allocator.free(string_buffer);
std.log.info("DECOMPRESSED len={d}, result={s}", .{ string_buffer.len, string_buffer });
//Now we need to convert to UTF16LE and fill exporting parameter
var string_utf16le = std.unicode.utf8ToUtf16LeAlloc(allocator, string_buffer) catch unreachable;
defer allocator.free(string_utf16le);
if (func_handle.SetString(W("DECOMPRESSED"), string_utf16le.ptr, @intCast(string_utf16le.len), error_info) != .ok) {
rfc.PrintErrorToLog(error_info) catch unreachable;
return .invalid_parameter;
}
return .ok;
},
else => {
//Empty compressed, so return empty uncompressed
return .ok;
},
}
}
pub fn main() !void {
var conn_params: [3]rfc.connection.Parameter = undefined;
var error_info: rfc.ErrorInfo = undefined;
// Must be registered in some connection in SM59 as Type=T, and registered as a program server with the same Program ID=ZIG_RFC_SERVER
//
// You also need to allow the server to register itslef via:
// Transaction SMGW -> Goto -> Expert Functions -> External Security -> Maintenance of ACL files -> RegInfo File
// Parameters: P/D=P and TP=ZIG_RFC_SERVER, rest can be * for testing but should be filled for security with correct data
conn_params[0].name = W("PROGRAM_ID");
conn_params[0].value = W("ZIG_RFC_SERVER");
conn_params[1].name = W("gwhost");
conn_params[1].value = W("*");
conn_params[2].name = W("gwserv");
conn_params[2].value = W("*");
if (rfc.server.RfcInstallServerFunction(null, try createFuncDesc(), ZIG_RFC_DECOMPRESS, &error_info) != .ok) {
try rfc.PrintErrorToLog(&error_info);
return;
}
var conn = rfc.connection.RfcRegisterServer(&conn_params, 3, &error_info) orelse {
try rfc.PrintErrorToLog(&error_info);
return;
};
std.log.info("Listening...", .{});
while (true) {
const result = conn.ListenAndDispatch(30, &error_info);
switch (result) {
.ok => {
std.log.info("Processed function call, listening again...", .{});
},
.retry => {
std.log.info("No function call, listening again...", .{});
},
.abap_exception => {
std.log.info("Function call ended with error, listening again...", .{});
},
else => {
std.log.err("Unexpected ErrorCode={s}", .{std.enums.tagName(rfc.ReturnCode, result).?});
try rfc.PrintErrorToLog(&error_info);
return;
},
}
}
}