I'm using JNA to access some dll function from Java, this dll Native Function is declared as the following:
unsigned int ts3client_initClientLib(const struct ClientUIFunctions* functionPointers, const struct ClientUIFunctionsRare* functionRarePointers, int usedLogTypes, const char* logFileFolder, const char* resourcesFolder);
and so, I declared it inside library interface as the following:
int ts3client_initClientLib(Structure functionPointers, Structure functionRarePointers, int usedLogTypes, String logFileFolder, String resourcesFolder);
and then I call it the following way:
ts3client_initClientLib(null, null, 1, "log.log", "soundbackends");
but I'm getting the following Exception:
Exception in thread "main" java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeInt(Native Method)
at com.sun.jna.Function.invoke(Function.java:383)
at com.sun.jna.Function.invoke(Function.java:315)
at com.sun.jna.Library$Handler.invoke(Library.java:212)
at com.sun.proxy.$Proxy0.ts3client_initClientLib(Unknown Source)
at pl.edu.tirex.ts3musicbot.MusicBot.main(MusicBot.java:17)
Correct solution:
Create event interface:
import com.sun.jna;
public interface ServerErrorEvent implements Callback
{
void invoke(long serverConnectionHandlerID, String errorMessage, int error, String returnCode, String extraMessage);
}
Create Client UI Functions Structure:
import com.sun.jna.Structure;
public class EventsStructure extends Structure
{
public ServerErrorEvent onServerErrorEvent;
#SuppressWarnings("rawtypes")
#Override
protected List<String> getFieldOrder()
{
List<String> fields = new ArrayList<String>();
for (Field f : this.getClass().getDeclaredFields())
{
boolean has = false;
for (Class<?> c : f.getType().getInterfaces())
{
if (c.equals(DefaultEvent.class))
{
has = true;
}
}
if (has)
{
fields.add(f.getName());
}
}
return fields;
}
}
Use following methods:
EventsStructure eventStructure = new EventsStructure();
eventStructure.onServerErrorEvent = new ServerErrorEvent() //implement server error event
{
#Override
public void invoke(long serverConnectionHandlerID, String errorMessage, int error, String returnCode, String extraMessage)
{
System.out.println("server error");
}
};
ts3client_initClientLib(eventStructure, null, 0, "logs", "soundbackends"); //invoke ts3client_initClientLib function from library
Related
Hi
In the game Minecraft you have to send data between the client and the server in order to sync stuff, one of the things that need to be synced in particular circumstances is TileEntities with a render. The data is stored on the server and sent to the client which is thereafter used for render.
You create a class that contains the data that needs to be synced and attach it to a channel creation API called "SimpleImpl". My Network object and a Message Object:
public class IntercraftPacketHandler
{
private static int index = 1;
private static final ResourceLocation CHANNEL_NAME = new ResourceLocation(Reference.MODID,"network");
private static final String PROTOCOL_VERSION = new ResourceLocation(Reference.MODID,"1").toString();
public static SimpleChannel getNetworkChannel()
{
final SimpleChannel channel = NetworkRegistry.ChannelBuilder.named(CHANNEL_NAME)
.clientAcceptedVersions(version -> true)
.serverAcceptedVersions(version -> true)
.networkProtocolVersion(() -> PROTOCOL_VERSION)
.simpleChannel();
// Sync Capability Identity Hidden data message.
channel.messageBuilder(MessageIdentityHidden.class,index)
.encoder(MessageIdentityHidden::encode)
.decoder(MessageIdentityHidden::decode)
.consumer(MessageIdentityHidden::handle)
.add(); index++;
// Send TreeTapTileEntity data to client.
channel.messageBuilder(MessageTreeTap.class,index)
.encoder(MessageTreeTap::encode)
.decoder(MessageTreeTap::decode)
.consumer(MessageTreeTap::handle)
.add(); index++;
// Send ChunkLoaderTileEntity data to client.
channel.messageBuilder(MessageChunkLoader.class,index)
.encoder(MessageChunkLoader::encode)
.decoder(MessageChunkLoader::decode)
.consumer(MessageChunkLoader::handle)
.add(); index++;
return channel;
}
}
public class MessageChunkLoader
{
private BlockPos pos;
private boolean canLoad;
public MessageChunkLoader(BlockPos pos,boolean canLoad)
{
this.pos = pos;
this.canLoad = canLoad;
}
public void handle(Supplier<NetworkEvent.Context> ctx)
{
ctx.get().enqueueWork(() -> {
try {
ChunkLoaderBaseTileEntity tile = (ChunkLoaderBaseTileEntity) Minecraft.getInstance().world.getTileEntity(pos);
tile.canLoad = canLoad;
} catch (NullPointerException err) {
System.out.println(String.format("Could not find ChunkLoaderTileEntity at %s %s %s!",pos.getX(),pos.getY(),pos.getZ()));
}
});
}
public static void encode(MessageChunkLoader message, PacketBuffer buffer)
{
buffer.writeBlockPos(message.pos);
buffer.writeBoolean(message.canLoad);
}
public static MessageChunkLoader decode(final PacketBuffer buffer)
{
return new MessageChunkLoader(buffer.readBlockPos(),buffer.readBoolean());
}
}
I then initialize it in my main mod class used by objects in my mod project.
#Mod(Reference.MODID)
public class IntercraftCore
{
public static final SimpleChannel NETWORK = IntercraftPacketHandler.getNetworkChannel();
...
The problem and this post's question; right now I create a new message class from the formula I follow in MessageChunkLoader (public static encode & decode method and a handle method). I would like to create a more generic class for creating message classes for TileEntities, but I'm having problems with that. Here's the current class:
public abstract class MessageTileEntity<T extends TileEntity>
{
protected final BlockPos pos;
protected final Class<T> clazz;
public MessageTileEntity(BlockPos pos, Class<T> clazz)
{
this.pos = pos;
this.clazz = clazz;
}
public abstract void handle(Supplier<NetworkEvent.Context> ctx);
protected T getTileEntity()
{
try {
return clazz.cast(Minecraft.getInstance().world.getTileEntity(pos));
} catch (NullPointerException e) {
System.out.println(String.format("Could not find %s at [%d %d %d]!",clazz.getSimpleName(),pos.getX(),pos.getY(),pos.getZ()));
throw e;
} catch (ClassCastException e) {
System.out.println(String.format("TileEntity at [%d %d %d] is not %s!",pos.getX(),pos.getY(),pos.getZ(),clazz.getSimpleName()));
throw e;
}
}
public static void encode(MessageTileEntity message, PacketBuffer buffer)
{
}
public static MessageTileEntity decode(final PacketBuffer buffer)
{
return null;
}
}
The main problem is I lack the proper Java skills to make it like I want it to function. The method handle is easy as it's non-static and needs to be custom to every TileEntity message, but the methods encode and decode which needs to be static gives me problems. I have no idea what I'm trying to achieve is possible, asking won't hurt. Maybe the solution is easier than I think.
i use jna 4.2.1
I have a method in the dll which returns a pointer to an object (C++)
basic_hash* getAlgorithmInstance( int algorithm )
basic_hash has the following methods (C++):
void reset ();
void partial (const byte* data, uint64 size);
void finalize (vector_byte& hash);
void hash (const byte* data, uint64 size, vector_byte& hash).
i have interface (java)
public interface HAL extends Library {
HAL INSTANCE = (HAL) Native.loadLibrary(
(Platform.isWindows() ? "HAL" : "libHAL"), HAL.class);
BasicHash getAlgorithmInstance(int i);
}
public static class BasicHash extends Structure {
public BasicHash() {}
public BasicHash(Pointer p) {
super(p);
read();
}
#Override
protected List getFieldOrder() {
return Arrays.asList(new String[] { "reset", "hash", "partial", "finalize" });
}
public interface Reset extends Callback { public void invoke();}
public Reset reset;
public interface Hash extends Callback {public void invoke(byte[] data, long size, byte[] hash);}
public Hash hash;
public interface Partial extends Callback {public void invoke(Pointer data, long size);}
public Partial partial;
public interface Finalize extends Callback {public void invoke(byte[] hash);}
public Finalize finalize;
}
and when I use the method without parameters in main()
HAL lib = HAL.INSTANCE;
BasicHash b = lib.getAlgorithmInstance(0);
b.reset.invoke();
I get an error:
Exception in thread "main" java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeVoid(Native Method)
at com.sun.jna.Function.invoke(Function.java:374)
at com.sun.jna.Function.invoke(Function.java:323)
at com.sun.jna.Function.invoke(Function.java:275)
at com.sun.jna.CallbackReference$NativeFunctionHandler.invoke(CallbackReference.java:646)
at com.sun.proxy.$Proxy1.invoke(Unknown Source)
at net.erver.ItServer.main(ItServer.java:79)
Why did I receive this error if the method resets the variables within the library? the method itself fulfills without problems (according to developers dll)
EDIT:
vector_byte has definition:
typedef unsigned char byte;
typedef std::vector< byte > vector_byte
and basic_hash has definition:
namespace HAL { namespace algorithms {
HAL_HASH_API enum class State : byte {
Normal,
Finished,
};
class HAL_HASH_API basic_hash
{
public:
virtual ~basic_hash() {}
virtual void reset() = 0;
virtual void partial( const byte*, uint64 ) = 0;
virtual void finalize( vector_byte& ) = 0;
virtual void hash( const byte*, uint64, vector_byte& ) = 0;
bool isFinished() {
return ( _state == State::Finished ? true : false );
}
protected:
State _state;
};
}
}
You need to use plain vanilla struct to pass data between JNA and your library. A C++ class (including a vector template) has a much different memory layout than does a simple C struct.
Running into a maddening blocking error:
Exception in thread "main" java.lang.Error: Structure.getFieldOrder() on class com.luke.generator.GeneratorEngine$VERSION_INFO returns names ([BuildString, ProtocolMajorVer, ProtocolMinorVer]) which do not match declared field names ([BiuldString, ProtocolMajorVer, ProtocolMinorVer])
at com.sun.jna.Structure.getFields(Structure.java:925)
at com.sun.jna.Structure.deriveLayout(Structure.java:1058)
at com.sun.jna.Structure.calculateSize(Structure.java:982)
at com.sun.jna.Structure.calculateSize(Structure.java:949)
at com.sun.jna.Structure.allocateMemory(Structure.java:375)
at com.sun.jna.Structure.<init>(Structure.java:184)
at com.sun.jna.Structure.<init>(Structure.java:172)
at com.sun.jna.Structure.<init>(Structure.java:159)
at com.sun.jna.Structure.<init>(Structure.java:151)
at com.luke.generator.GeneratorEngine$.<init>(GeneratorEngine.java:108)
at com.luke.generator.connectionVersion(GeneratorEngine.java:297)
at com.luke.generator.Main.main(Main.java:161)
Platform: Intel, Windows 8
JRE 1.7, 32-bit (x86)
Eclipse Kepler, Default encoding UTF-8
jna-4.1.0.jar
32-bit CPP DLL - I can confirm that I am loading the library and calling functions that do not include parameters. I also tried passing WStrings and Strings, but that did not address the issue.
Source:
CPP struct:
typedef struct {
UINT32 ProtocolMajorVer;
UINT32 ProtocolMinorVer;
UI_STRING BuildString; // Build version for the application.
} VERSION_INFO;
CPP Function
DLL_EXPORTS RETURN_TYPES ConnectionVersion (VERSION_INFO &Version) {<body omitted>}
Java code:
//Interface definition
public interface UiApi extends StdCallLibrary {
UiApi INSTANCE = (UiApi) Native.loadLibrary(UiApiPath,UiApi.class);
final String PROTOCOLMAJORVERSION = new String("ProtocolMajorVer");
final String PROTOCOLMINORVERSION = new String("ProtocolMinorVer");
final String BUILDSTRING = new String("BuildString");
public static class VERSION_INFO extends Structure {
public static class ByReference extends VERSION_INFO implements Structure.ByReference {}
public static class ByValue extends VERSION_INFO implements Structure.ByValue {}
public int ProtocolMajorVer;
public int ProtocolMinorVer;
public byte[] BiuldString;
protected List getFieldOrder() {
return Arrays.asList(new String[] { "ProtocolMajorVer","ProtocolMinorVer","BuildString" });
}
}
//Connection
public int Connect(byte[] strServerName);
public int Disconnect();
public int CloseProject();
public int ConnectionVersion(VERSION_INFO result);
public int ConnectionVersion(VERSION_INFO.ByReference result);
public int ConnectionVersion(VERSION_INFO.ByValue result);
}
//Engine.connectionVersion() method
public static int connectionVersion() {
int nReturn = 0;
String str = new String("This is my build version\n");
UiApi uiapilib;
uiapilib = UiApi.INSTANCE;
try {
UiApi.VERSION_INFO.ByReference result = new UiApi.VERSION_INFO.ByReference();
result.ProtocolMajorVer = 0;
result.ProtocolMinorVer = 0;
result.BiuldString = str.getBytes();
nReturn = uiapilib.ConnectionVersion(result);
}
catch (Exception e) {
System.out.println("Error=" + e.getLocalizedMessage());
}
return nReturn;
}
//This is the code in main that results in exception
private static Engine engine;
engine = new GeneratorEngine();
engine.connectionVersion();
I must be missing something basic. Is there something in Eclipse that could possibly be causing the HashSet name comparisons in JNA's Structure.java (line 925) that would result in names not matching? From the exception thrown, these definitions look identical.
Thanks for any tips, guidance, fresh perspectives you can offer.
Check your spelling - the field is called BiuldString not BuildString, you have the i and u reversed.
So i have native function in C declared as int NgSetEvent (int event, long callback). long Callback represent the callback function pointer. Callback prototype is declared as int oncodeline(int code, int documentid, char *string). The callback should be registered with method NgSetEvent. The problem is how to get pointer to that function which should be long or Nativelong? I have tried a lot of different aproaches but none of them gave a result. Callback was never invoked.
I have tried to do like it says in Turn a Callback into a Pointer in JNA, but with no success. i don't know what to try anymore.
Any help will be appreciated.
OnCodeline
public interface OnCodeline extends Callback {
int oncodeline (int code, int documentid, byte[] string);
}
OnCodelineStruct
public class OnCodelineStruct extends Structure {
public OnCodeline onc;
#Override
protected List getFieldOrder() {
return Arrays.asList(new String[] { "onc" });
}
}
Main class
OnCodelineStruct onCodelinStruct;
onCodelinStruct = new OnCodelineStruct();
onCodelinStruct.onc = new OnCodeline() {
#Override
public int oncodeline(int code, int documentid, byte[] string) {
System.out.println("This IS a CALLBACK!");
return 0;
}
};
sbDll.NgSetEvent(0, onCodelinStruct.getPointer().getNativeLong(0));
Is it possible to map The following Macro function with JNA?
int ListView_FindItem(
HWND hwnd,
int iStart,
const LPLVFINDINFO plvfi
);
I've tried to map this function with StdCallLibraryb, but that does not seem to work
(Function not found exception is thrown).
Basically i am trying find the index of a particular item in List view of desktop.
I have the name of the item i intend to find.
EDIT:
i have tried using the send message feature, i get the following exception
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function
'GetMessage': The specified procedure could not be found.
at com.sun.jna.Function.<init>(Function.java:179)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:347)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:327)
at com.sun.jna.Library$Handler.invoke(Library.java:203)
at $Proxy0.GetMessage(Unknown Source)
at javaapplication4.Main.main(Main.java:43)
Java Result: 1
This is the code i used
public class Main {
public static class LVFINDINFO extends Structure {
public int flags =1002;
public String psz = "new folder3";
public LPARAM lParam ;
public POINT pt;
public int vkDirection;
}
public static class MSG extends Structure {
public HWND hWnd;
public int message;
public int wParam =-1;
public LVFINDINFO lParam1;
public int time;
public POINT pt;
public MSG(LVFINDINFO lParam) {
lParam1 = lParam;
}
}
public static void main(String[] args) {
User32 user32 = (User32) Native.loadLibrary("User32", User32.class);
LVFINDINFO i = new LVFINDINFO();
MSG m = new MSG(i);
user32.GetMessage(m, user32.GetDesktopWindow(), 0, 0);
System.out.println(user32.GetMessage(m, user32.GetDesktopWindow(), 0, 0));
}
}
Since a macro exists purely at compile-time, there's no way to call it using JNA.
You'll need to see what the macro actually does and do that instead. According to the documentation it sends the LVM_FINDITEM message. You need to find out how to send that message using JNA.