Using a ByteBuffer to represent a string in a JNA call results in extra characters in the buffer - jna

I'm calling a dll using JNA and code generated using Jnaerator. One of the methods requires an string, and the JNA signature takes a ByteBuffer.
I've tried allocating the ByteBuffer as direct (ByteBuffer.allocateDirect) and indirect (ByteBuffer.wrap) but in both cases some times the string that reaches the dll has additional random characters (e.g. ReceiptÚeœ ). The original byte[] is there (receipt = 52 65 63 65 69 70 74) but as well a variable number of additional random bytes (01 da 65 9c 19). Randomly the string is correct, with no additional bytes.
I've tried the equivalent code using BridJ instead of JNA (the method signature takes then a Pointer name) and it in that case it works fine. Unfortunately I can't switch to BridJ because I need to use the com.sun.jna.platform.win32 classes, unless I can generate a BridJ replacement for those (https://stackoverflow.com/questions/31658862/jnaerator-bridj-user32-missing-methods)
Native declaration:
HRESULT extern WINAPI WFSOpen ( LPSTR lpszLogicalName, HAPP hApp, LPSTR lpszAppID,DWORD dwTraceLevel, DWORD dwTimeOut, DWORD dwSrvcVersionsRequired, LPWFSVERSION lpSrvcVersion, LPWFSVERSION lpSPIVersion, LPHSERVICE lphService);
JNAerator JNA code:
//works
#Deprecated
NativeLong WFSOpen(Pointer lpszLogicalName, Pointer hApp, Pointer lpszAppID, int dwTraceLevel, int dwTimeOut, int dwSrvcVersionsRequired, WFSVERSION lpSrvcVersion, WFSVERSION lpSPIVersion, ShortByReference lphService);
//does not work
NativeLong WFSOpen(ByteBuffer lpszLogicalName, Pointer hApp, ByteBuffer lpszAppID, int dwTraceLevel, int dwTimeOut, int dwSrvcVersionsRequired, WFSVERSION lpSrvcVersion, WFSVERSION lpSPIVersion, ShortBuffer lphService);
Java call working (but deprecated)
Pointer m = new Memory(string.length() + 1); // WARNING: assumes ascii-only string
m.setString(0, string);
MsxfsLibrary.INSTANCE.WFSOpen(lpszLogicalName, lphApp.getValue(), lpszAppID, dwTraceLevel, dwTimeOut, dwSrvcVersionsRequired, lpSrvcVersion, lpSPIVersion, lphService);
Java call NOT working test A:
lpszLogicalName = ByteBuffer.wrap(bytes);
MsxfsLibrary.INSTANCE.WFSOpen(lpszLogicalName, lphApp.getValue(), lpszAppID, dwTraceLevel, dwTimeOut, dwSrvcVersionsRequired, lpSrvcVersion, lpSPIVersion, lphService);
Java call NOT working test B:
byte[] bytes = string.getBytes();
return ByteBuffer.wrap(bytes);
ByteBuffer bb = ByteBuffer.allocateDirect(bytes.length);
bb.put(bytes);
lpszLogicalName = bb.position(0);
msxfsLibrary.WFSOpen(lpszLogicalName, lphApp.getValue(), lpszAppID, dwTraceLevel, dwTimeOut, dwSrvcVersionsRequired, lpSrvcVersion, lpSPIVersion, lphService);

If you're referring to _wfsopen(), it's expecting a wide-character string. Either use WString, or configure your library to type-map String (see W32APIOptions.UNICODE_OPTIONS).

I think what's happening is that you are passing an array of bytes that contains your string but it is not null terminated string. You should create a byte array with an extra position. Set that las position to 0 and copy your string into that byte array.

Related

How to convert a luajit pointer to a string and back?

I need some help converting a luajit pointer to a string and back.
First I define the ctype:
ffi.cdef[[
typedef struct {
unsigned char Bytes[16];
} EncryptionKeys[100000000];
void* malloc(size_t);
void free(void*);
]]
Then use malloc to allocate some memory and then create the 'EncryptionKeys' variable.
local EncryptionKeyMemoryAddress = ffi.C.malloc(ffi.sizeof("EncryptionKeys"))
local EncryptionKeys = ffi.cast("EncryptionKeys(&)", EncryptionKeyMemoryAddress)
I first convert the variable into a lua string using:
ffi.string(EncryptionKeyMemoryAddress)
But I can't figure out how to convert it back!
Can someone please help me?
FYI: I am passing the 'EncryptionKeyMemoryAddress' variable to one of the function parameters for a lua lane (https://lualanes.github.io/lanes/).
Edit:
Here is the section of code that I am working on:
This is for the client managers module of my server that manages a list of lua states that all have access to any clients connected to the server. They all use a shared section of memory that I want them to have access to using a pointer.
local ClientFFIString = [[
typedef struct {
unsigned char Bytes[16];
} EncryptionKeys[100000000];
void* malloc(size_t);
void free(void*);
]]
ffi.cdef(Matchpools.FFIString)
local EncryptionKeyMemoryAddress = ffi.C.malloc(ffi.sizeof("EncryptionKeys"))
--------------------------------------------
function ClientManagers.CreateNewClientManager()
local EncryptionKeys = ffi.cast("EncryptionKeys(&)", EncryptionKeyMemoryAddress)
EncryptionKeys[0].Bytes[0] = 24
print("___a", EncryptionKeys[0].Bytes[0])
local NewIndex = #ClientManagers.List+1
ClientManagers.List[NewIndex] = ClientManagerFunc(
ClientFFIString,
ffi.string(EncryptionKeysMemoryAddress)
)
end
--------------------------------------------
local ClientManagerFunc = Lanes.gen("*", function(ClientFFIString, EncryptionKeysMemoryAddress)
ffi = require("ffi")
ffi.cdef(ClientFFIString)
local EncryptionKeys = ffi.cast("EncryptionKeys(&)", EncryptionKeyMemoryAddress)
print("___a", EncryptionKeys[0].Bytes[0])
-- I want this to be 24 just like it is in the function that created this lua state
local ClientManagerRunning = true
while ClientManagerRunning do
--local dt = GetDt()
--UpdateClientData(dt)
--UpdateMatchmaking(dt)
end
end)
You can convert Lua string to your structure pointer (and later use it as an array):
ffi.cdef"typedef struct {unsigned char Bytes[16];} Key;"
ptr=ffi.cast("Key *", your_string)
print("First Byte of the First Key in your Array:", ptr[0].Bytes[0])
UPDATE:
Let's test how it works for an array containing three keys:
local ffi = require'ffi'
ffi.cdef"typedef struct {unsigned char Bytes[16];} Key;"
local your_string = string.char(11):rep(16)..string.char(22):rep(16)..string.char(33):rep(16)
local ptr=ffi.cast("Key *", your_string)
print("First Byte of the First Key in your Array:", ptr[0].Bytes[0])
print("First Byte of the Second Key in your Array:", ptr[1].Bytes[0])
print("First Byte of the Third Key in your Array:", ptr[2].Bytes[0])
It prints 11, 22, 33
UPDATE 2:
Pass the address of buffer instead of the content of buffer
In the main thread
-- allocate the buffer
local Keys = ffi.cast("EncryptionKeys&", ffi.C.malloc(ffi.sizeof("EncryptionKeys")))
-- write to the buffer
Keys[2].Bytes[5] = 42
-- create string containing 64-bit address
local string_to_send = tostring(ffi.cast("uint64_t", Keys))
-- Send string_to_send to a lane
Inside the lane
-- receive the string
local received_string = .....
-- restore the buffer pointer
local Keys = ffi.cast("EncryptionKeys&", loadstring("return "..received_string)())
-- read the data from the buffer
print(Keys[2].Bytes[5])

F# Passing Nulls to Unmanaged Imported DLL

In F# i'm using an external DLL (in this case SDL Graphics library) I'm importing the method I require as follows...
[<DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl)>]
extern int SDL_QueryTexture(nativeint texture, uint32& format, int& access, int& w, int& h)
This works fine and I can successfully call the method using the following...
let result = SDLDefs.SDL_QueryTexture(textTexture, &format, &access, &w, &h)
The problem is that the native SDL methods accept null values for many pointer arguments. This is required in some scenarios (which function like overloaded methods). I can't find any way to call these methods from F# passing nulls.
For example, this fails with "does not have null as proper value"
let result = SDLDefs.SDL_QueryTexture(textTexture, &format, null, &w, &h)
I read about the attribute [AllowNullLiteral] but it seems like I can only apply it to types I define, and not pre-defined types which are used in my imported DLL.
Is there any way I can do this?
If you want to specify nulls, you need to use "raw pointers", which are represented by types nativeint and nativeptr<T>.
[<DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl)>]
extern int SDL_QueryTexture(nativeint texture, uint32& format, nativeint access, int& w, int& h)
// Call without null
let access = 42
let pAccess = NativePtr.stackalloc<int> 1
NativePtr.write pAccess access
SQL_QueryTexture( textTexture, &format, NativePtr.toNativeInt pAccess, &w, &h )
let returnedAccess = NativePtr.read pAccess
// Call with null
SQL_QueryTexture( textTexture, &format, null, &w, &h )
NOTE: be careful with stackalloc. Allocating memory on the stack is quite handy, because you don't need to explicitly release it, but pointers to it will become invalid once you exit the current function. So you can only pass such pointers to an external function if you're sure that the function won't store the pointer and try to use it later.
If you need to pass a pointer to real heap memory that's not going anywhere, you'll need Marshal.AllocHGlobal. But don't forget to release! (or else :-)
let access = 42
let pAccess = Marshal.AllocHGlobal( sizeof<int> )
NativePtr.write (NativePtr.ofNativeInt pAccess) access
SQL_QueryTexture( textTexture, &format, pAccess, &w, &h )
Marshal.FreeHGlobal( pAccess )

cant copy the yytext with strcpy

This is my code
strcpy doesnt copy the yytext
Any suggestion why?
These are my globals
char** v; /* array of variables and their values */
int i;
some name definition
WORD [a-zA-Z]
DIGIT [0-9]
These are the states
<VAL>"\"".+"\"" {int x=sizeof(yytext);
v[i]=(char*)realloc(v, x*sizeof(char));
strcpy(v[i],yytext);
i++;
BEGIN(INITIAL);}
<VAL>. { i--;printf("error");}
<SAVE_VAL>{WORD}[{WORD}{DIGIT}_]* {
if (NULL==v){
v=(char**)realloc(v, 100*sizeof(char*));
i=0;
}
int x=sizeof(yytext);
v[i]=(char*)realloc(v, x+2);
strcpy(v[i],yytext);
i++;
BEGIN(VAL);
}
val" " {BEGIN(SAVE_VAL);}
This is the yywrap
int yywrap(void){
return 1;
}
This is the main
int main(void) {
yyin=fopen("input.txt","r");
yylex();
fclose(yyin);
This is the loop to print the strings
for (int j=0;j<100;j++){
if (NULL==v[j])
break;
printf("%s",v[j],i);
}
}
}
I'm tring to run it on
val a="asdasd";
I'm expecting it to print
a
asdasd
This:
int x=sizeof(yytext);
doesn't do what you think it does. yytext is of type char*; that is, it is a pointer to a character. That means that sizeof yytext is either 4 or 8, depending on whether you're compiling on a 32-bit or 64-bit platform. (Or some other pointer length if you have a really unusual architecture.)
It is not the length of the string starting at yytext. That would be the standard library function strlen(yytext).
However, you don't need to use that, either, because flex has helpfully assigned the length of the token to the global variable yyleng.
When you are allocating space for the copy, don't forget that in C strings are always NUL-terminated. That means that the allocation for a string must be (at least) one greater than the length of the string, which means you need yyleng + 1 bytes.

Lua Non blocking read acces on empty FIFO

I am writing a little lua script that read data from a FIFO.for this I use the classical:
f=assert(io.open("/tmp/myfifo")
f:read()
When the fifo is empty/not feeded, my script block. Is there a way to avoid this ?.
There is no straight forward Lua-only method I guess. With luajit http://luajit.org/ (which provides ffi) it is possible:
local ffi = require'ffi'
--- The libc functions used by this process.
ffi.cdef[[
int open(const char* pathname, int flags);
int close(int fd);
int read(int fd, void* buf, size_t count);
]]
local O_NONBLOCK = 2048
local chunk_size = 4096
local buffer = ffi.new('uint8_t[?]',chunk_size)
local fd = ffi.C.open('mypipe',O_NONBLOCK)
local nbytes = ffi.C.read(fd,buffer,chunksize)
-- .. process data

How to call a Delphi DLL from VB6

Given the following Delphil DLL declaration
function csd_HandleData(aBuf: PChar; aLen: integer): integer; stdcall;
what would be the VB6 declaration to use it?
I've tried a variety of declarations, e.g.
Declare Function csd_HandleData Lib "chsdet.dll" (ByVal aBuf As String, ByVal aLen As Integer)
Declare Function csd_HandleData Lib "chsdet.dll" (aBuf As Long, ByVal aLen As Integer)
Declare Function csd_HandleData Lib "chsdet.dll" (aBuf As Byte, ByVal aLen As Integer)
with the relevant code to suit the parameters, but nothing seems to work, i.e. the Delphi debugger says I have a too-largish value in aLen and a null string in aBuf.
I am working toward using a TypeLib to drive the connection, but was prototyping with Declares.
try
Declare Function csd_HandleData Lib "chsdet.dll" (ByVal aBuf As String,
ByVal aLen As Integer) As Integer
Seems you forgot the return value.
VB integer datatype is 16bit, so you should declare it as long which is equivalent to integer in Delphi and other languages.
Declare Function csd_HandleData Lib "chsdet.dll" (ByVal aBuf As String, ByVal aLen As long) as long
For those interested, here's the final IDL for the typelib for CHSDET. What impressed me (after re-discovering Matt Curland's EditTLB tool) was that I can put structures into a typelib, and VB handles them as if I'd declared them in the source code.
I've written to the author of ChsDet and perhaps this will end up as part of the standard distro.
// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: chsdet.tlb
[
uuid(316A83D7-8BF4-490E-BDDE-75EBC332C355),
version(1.0),
helpstring("Charset Detector - as the name says - is a stand alone executable module for automatic charset detection of a given text.\r\n\t\r\nIt can be useful for internationalisation support in multilingual applications such as web-script editors or Unicode editors.\r\n\t\r\nGiven input buffer will be analysed to guess used encoding. The result can be used as control parameter for charset conversation procedure.\r\n\t\r\nCharset Detector can be compiled (and hopefully used) for MS Windows (as dll - dynamic link library) or Linux.\r\n\t\r\nBased on Mozilla's i18n component - http://www.mozilla.org/projects/intl/. \r\n\r\nCharset Detector is open source project and distributed under Lesser GPL.\r\nSee the GNU Lesser General Public License for more details - http://www.opensource.org/licenses/lgpl-license.php\r\n\r\nNikolaj Yakowlew \xFFFFFFA9 2006-2008 \r\nTypeLib by Bruce M. Axtens, 2008.")
]
library CHSDET
{
// TLib : // Forward declare all types defined in this typelib
[
dllname("CHSDET.dll"),
version(1.0),
helpstring("Functions in CHSDET.DLL")
]
module CHSDETFunctions {
[entry(0x60000000), helpstring("Returns rAbout record (qv)")]
void _stdcall GetAbout([in, out] rAbout* AboutRec);
[entry(0x60000001), helpstring("Reset detector. Prepares for new analysis.")]
void _stdcall Reset();
[entry(0x60000002), helpstring("Analyse given buffer of specified length.
Return value is of eHandleDataErrors, either
NS_ERROR_OUT_OF_MEMORY (Unable to create internal objects) or NS_OK.
Function can be called more that one time to continue guessing. Charset Detector remembers last state until Reset called.")]
void _stdcall HandleData(
[in] BSTR aBuf,
[in] short aLen,
[out, retval] short* retVal);
[entry(0x60000003), helpstring("Returns either TRUE (Charset Detector is sure about text encoding.) or FALSE.
NB: If input buffer is smaller then 1K, Charset Detector returns FALSE.")]
void _stdcall IsDone([out, retval] short* retVal);
[entry(0x60000004), helpstring("Signal data end. If Charset Detector hasn't sure result (IsDone = FALSE) the best guessed encoding will be set as result.")]
void _stdcall DataEnd();
[entry(0x60000005), helpstring("Returns guessed charset as rCharsetInfo record")]
void _stdcall GetDetectedCharset([out, retval] rCharsetInfo* retVal);
[entry(0x60000006), helpstring("Returns all supported charsets in form "0x0A Name - CodePage"")]
void _stdcall GetKnownCharsets(
[in, out] long* sList,
[out, retval] long* retVal);
[entry(0x60000007), helpstring("Return eBOMKind value matching byte order mark (if any) of input data.")]
void _stdcall GetDetectedBOM([out, retval] eBOMKind* retVal);
[entry(0x60000008), helpstring("Remove CodePage from consideration as a possible match")]
void _stdcall DisableCharsetCP([in] long CodePage);
};
typedef [uuid(91694067-30AB-44A9-A210-F5602935475F)]
struct tagrAbout {
long lMajor;
long lMinor;
long lRelease;
long sAbout;
} rAbout;
typedef [uuid(3C8B7420-D40B-458B-8DE8-9B3D28607396)]
enum {
BOM_Not_Found = 0,
BOM_UCS4_BE = 1,
BOM_UCS4_LE = 2,
BOM_UCS4_2143 = 3,
BOM_UCS4_3412 = 4,
BOM_UTF16_BE = 5,
BOM_UTF16_LE = 6,
BOM_UTF8 = 7
} eBOMKind;
typedef [uuid(9B231DEF-93FB-440D-B06B-D760AECE09D0)]
struct tagrCharsetInfo {
long Name;
short CodePage;
long Language;
} rCharsetInfo;
typedef enum {
NS_OK = 0,
NS_ERROR_OUT_OF_MEMORY = -2147024882
} eHandleDataErrors;
};
I don't know what a PChar is in Delphi, is it just one character? ASCII?? Unicode?
An Integer is 16 bits in VB6, you'll have to declare aLen as Long, which can hold 32 bits.
You also have to declare the return type of the function, in this case you'll want to return a Long value too.
This will probably work:
Declare Function csd_HandleData Lib "chsdet.dll" (aBuf As Byte, ByVal aLen As Long) As Long
I don´t know exactly how Vb works but PChar is a pointer, so try to get the reference instead of the value.
Declare Function csd_HandleData Lib "chsdet.dll" (**ByReference <--guessing here :D** aBuf As String, ByVal aLen As Integer)

Resources