Cannot get current foreground window title using Jython + JNA - java

I am trying to get the current foreground window title using JNA and Jython. My code is just a translation of a Java code to accomplish the same. Code in Java works - I have tested it - and was taken from here: https://stackoverflow.com/a/10315344/1522521
When I call GetLastError() I get 1400 code back, which is ERROR_INVALID_WINDOW_HANDLE - stating that the window handle is invalid. I am sure that window handle is correct, because I can successfully call GetWindowTextLength(); ShowWindow(handle, WinUser.SW_MINIMIZE); and ShowWindow(handle, WinUser.SW_MAXIMIZE); to get the length of the title (seems correct) and manipulate the window.
I have a hunch, that the problem is with how I use variable text as argument for GetWindowText(). According to JNA's Javadoc it suppose to be char[] buffer for JNA to copy the text. As I simply pass 'string' it may be incorrect. This is my code:
def get_current_window_text():
"""
Get current foreground window title.
"""
handle = User32.INSTANCE.GetForegroundWindow()
if User32.INSTANCE.IsWindowVisible(handle):
print "Text lenght:", User32.INSTANCE.GetWindowTextLength(handle)
max_length = 512
text = ''
result = User32.INSTANCE.GetWindowText(handle, text, max_length)
print "Copied text length:", result
if result:
print "Window text:", text
return result
else:
last_error_code = Kernel32.INSTANCE.GetLastError()
if last_error_code == Kernel32.ERROR_INVALID_WINDOW_HANDLE:
print "[ERROR] GetWindowText: Invalid Window handle!"
else:
print "[ERROR] Unknown error code:", last_error_code
else:
print "[ERROR] Current window is not visible"

My hunch was correct, the problems was with incorrect argument when calling GetWindowText(). It suppose to be char[] - not a Jython variable. This lead me to research more and find something I wasn't aware before - Java arrays in Jython. As stated in Jython documentation http://www.jython.org/archive/21/docs/jarray.html :
Many Java methods require Java array objects as arguments. The way that these arguments are used means that they must correspond to fixed-length, mutable sequences, sometimes of primitive data types. The PyArray class is added to support these Java arrays and instances of this class will be automatically returned from any Java method call that produces an array. In addition, the "jarray" module is provided to allow users of Jython to create these arrays themselves, primarily for the purpose of passing them to a Java method.
The documentation has the mapping table as well. The working code would be something like this:
import jarray
text_length = User32.INSTANCE.GetWindowTextLength(handle)
max_length = 512
text = jarray.zeros(text_length, 'c')
result = User32.INSTANCE.GetWindowText(handle, text, max_length)
print 'Copied text:', result

Related

JNA call with String behaves differently from one with byte[]

I have a JNA Java interface for a C function mpv_set_option_string defined as:
public interface MPV extends StdCallLibrary {
MPV INSTANCE = Native.loadLibrary("lib/mpv-1.dll", MPV.class, W32APIOptions.DEFAULT_OPTIONS);
long mpv_create();
int mpv_initialize(long handle);
int mpv_set_option_string(long handle, String name, String data);
}
When I call this like this:
System.setProperty("jna.encoding", "UTF8");
long handle = MPV.INSTANCE.mpv_create();
int error = MPV.INSTANCE.mpv_initialize(handle);
error = MPV.INSTANCE.mpv_set_option_string(handle, "keep-open", "always");
I get an error back (-5) from the last call, indicating the option (keep-open) is not found.
However, if I change the JNA function signature to:
int mpv_set_option_string(long handle, byte[] name, byte[] data);
...and then call it like this:
error = MPV.INSTANCE.mpv_set_option_string(
handle,
"keep-open\0".getBytes(StandardCharsets.UTF_8),
"always\0".getBytes(StandardCharsets.UTF_8)
);
...it returns no error (0) and works correctly (or so it seems).
What I don't get is, JNA is supposed to encode String by default as char * with UTF-8 encoding and NUL terminated (exactly what I do manually), yet I get different results.
Anyone able to shed some light on this?
You shouldn't be passing W32OPTIONS to a library that isn't a WIN32 API.
By default, JNA maps String to char*, so removing the options should fix the issue for you.
You should also be using an explicit native type for your handle instead of Java long. Pointer is probably correct in this case.
Looks like I found the issue, although I'm not 100% sure what is happening.
It seems that using W32APIOptions.DEFAULT_OPTIONS means it will use the UNICODE settings (because w32.ascii property is false). This looked okay to me, as mpv-1.dll works with UTF-8 strings only, which is Unicode.
However, now I'm guessing that in this case it means it will call a wide-char version of the library function (and if that doesn't exist, still call the original function), and probably means it encodes Strings with two bytes per character. This is because most Win32 libraries have an ASCII and WIDE version of methods accepting strings, but nothing for UTF-8.
Since mpv-1.dll only accepts UTF-8 (and isn't really Win32), strings should be just encoded as bytes in UTF-8 format (basically, just leave them alone). To let JNA know this, either donot pass a W32APIOptions map at all, or select the ASCII_OPTIONS manually.

Convert string into utf-8 in php

There is a script written in Java and I am trying to convert it into PHP, but I'm not getting the same output.
How can I get it in PHP same as in Java?
Script written in Java
String key = "hghBGJH/gjhgRGB+rfr4654FeVw12y86GHJGHbnhgnh+J+15F56H";
byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
Output: [B#167cf4d
Script written in PHP
$secret_key = "hghBGJH/gjhgRGB+rfr4654FeVw12y86GHJGHbnhgnh+J+15F56H";
utf8_encode("AWS4".$secret_key);
Output: AWS4hghBGJH/gjhgRGB+rfr4654FeVw12y86GHJGHbnhgnh+J+15F56H
The result [B#167cf4d you are getting is a toString() call on the byte array. The [B means the value is a byte array. #167cf4d is it's location within the virtual memory. You simply cannot get the PHP script to give you the same result. What you need to do is fix the printing on the Java side, but we don't know what API you're using to print it and where. Is it a web-application, a local application, etc... ?
edit:
Since you're using it in a Java web-application, there are two likely scenarios. The code is either in a Servlet, or in a JSP. If it's in a servlet, you have to set your output to UTF-8, then you can simply print the string like so:
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.write("AWS4");
out.write(key);
If it's in a JSP, that makes it a bit more inconvenient, because you have to make sure you don't leave white-spaces like new lines before the printing begins. It's not a problem for regular HTML pages, but it's a problem if you need precisely formatted output to the last byte. The response and out objects exist readily there. You will likely have some <%#page tags with attributes there, so you have to make sure you're opening the next one as you're closing the last. Also a new line on the end of file could skew the result, so in general, JSP is not recommended for white-space-sensitive data output.
You can't sysout an byte array without converting it to a string, UTF-8 by default, toString() is being called,when you do something like:
System.out.println("Weird output: " + byteArray);
This will output the garbage you mentioned above. Instead, create a new instance of the standard String class.
System.out.println("UTF-8: " + new String(byteArray));
In Java you get the object reference of the byte array, not its value. Try:
new String(kSecret, "UTF-8");

How to prvent Xtend templates from printing Variables during loop

I am using the Xtend templates to write a small program. I try using the IF statement, but every time I execute it it prints the variable in the console, which I dont want it to.
«IF x==y»
The jump value is «database.get(2)»
«jump_var_letter = String.charAt(1)»
«old_jump_ahd=database.get(2) »
«ENDIF»
Here the database is an array of integers and String is an array of letters. Here I just want it to print the value found at database.get(2) i.e 5. The last two expressions befor the ENDIF is meant for assignning a few values( which need not be printed)
The jump value is 5
Instead I get
The jump value is 5
D
5
Could somebody please tell me how I could stop printing the other two values.
Thank you in advance for your help..
After looking for sometime on the net I found that you could prevent the printing of the expreesions in between by using block expressions and then returning a null expression. (Although this method is not encouraged, I found that it provides me the result I wanted). So the expression I posted could be written as:
«IF x==y»
The jump value is «database.get(2)»
«{jump_var_letter = String.charAt(1); "" }»
«{old_jump_ahd=database.get(2); ""} »
«ENDIF»
This prints
The jump value is 5.

jvm crashes when trying to send TCHAR type to java function that accepts string as argument

While trying to send the name of the window on which currently a key is being pressed from a JNI C code to a java method jvm crashes. I think it is due to passing of an invalid argument. Please explain why the call fails and how can I send the argument ?
Prototype of java method looks like :
public void storeNameOfForegroundWindow(String windowName) {
// store in the list
}
JNI C snippet :
jmethodID callBackToStoreWindowName = (*env)->GetMethodID(env,cls,"storeNameOfForegroundWindow","(Ljava/lang/String;)V");
TCHAR title[500];
GetWindowText(GetForegroundWindow(), title, 500);
jvalue windowName,*warr;
windowName.l = title;
warr = &title;
(*Env)->CallVoidMethodA(Env,object,callBackToStoreWindowName,warr);
JVM crashes as it encounters the above snippet.I know that jvm crashes due to passing of invalid argument to the java function ( via C code) . If that is so please explain how do I send the argument .I need to send the title of the current window to the java function.
Since your method has a String as its argument, you should give it a jstring instance. JVM can not understand what a TCHAR is. So you need to convert your chars to a java string using:
(*env)->NewStringUTF(env, title);
EDIT: if TCHAR is wchar_t, i.e. is 16 bit and can be cast to a jchar, then you need to use NewString instead of NewStringUTF. You can read more here.
When I first see TCHAR, I say "Oh! It is magnificent, You can write one code that work in both Win9X and WinNT and also call best platform functions with just one definition: _UNICODE". But in time I see that this confuse many developers. There is nothing standalone as TCHAR, it is a typedef of char when _UNICODE is not defined and a typedef of wchar_t otherwise, so depending on project's definition it will change. But on the other hand Java method expect only one of them (either char or wchar_t, but I don't know which one of them), so if you define _UNICODE in your project (this will be done automatically in new IDE) and Java method expect a char* then you are passing a wchar_t* then you are passing a wrong type to the function and length of string will be counted as one (since wchar_t is 2 bytes, it map most of single byte char to char + an extra '\0') and if you pass a char* to function while it expect a wchar_t*, it may produce an error (for example access violation) because:
TCHAR title[500]; // will be converted to char title[500];
// now fill it and it may contain 'abcde\0 some junk data'
// Java want to interpret this as wchar_t* and expect '\0\0' as end of string
// since wchar_t is 2 byte and it may never see it in you string and read beyond
// end of title that is obviously an error!

Invoking AS400 RPG From Java

I have a very limitied (0) knowledge on AS400 and RPG. But we have a urgent requirement where we need to invoke a RPG program from a java class. So I found that we can achieve it through JTOpen. But I am stuck at declaring the ProgramParameter list. I have the following information about RPG Program
Program name: ZM30000R
Parameters:
Branch 7,0 (Numeric)
Account type 2 (01-cheque,02 savings)
Account Number 20 (character)
Error code 7 (character)
DR/CR indicater 1 (character D,C)
But no clue about what is the intput and output .How to declare the ProgramParameter. I have done as below. I cannot test as well because I dont have connectivity to these systems.
// Create AS400 Text objects for the different lengths
// of parameters you are sending in.
AS400Text branchTxt = new AS400Text(7);
AS400Text accntTypeTxt = new AS400Text(2);
AS400Text accntNumberTxt = new AS400Text(20);
AS400Text errorCodeTxt = new AS400Text(7);
AS400Text DCIndicatorTxt = new AS400Text(1);
// declare and instantiate your parameter list.
ProgramParameter[] parmList = new ProgramParameter[5];
// assign values to your parameters using the AS400Text class to convert to bytes
// the second parameter is an integer which sets the length of your parameter output
parmList[0] = new ProgramParameter( branchTxt.toBytes(branch),7);
parmList[1] = new ProgramParameter( accntTypeTxt.toBytes(accntTypeTxt),2);
parmList[2] = new ProgramParameter( accntNumberTxt.toBytes(accntNumberTxt),20);
parmList[3] = new ProgramParameter( errorCodeTxt.toBytes(""),7);
parmList[4] = new ProgramParameter( DCIndicatorTxt.toBytes(indicator),5);
Any help will be really highly useful.
Thanks and Regards,
Srinivas
Well, I do have a clue just by the description of the parameters. Branch, account type and account number are IN. You need that information for a financial booking or transaction. The error code is appearently OUT. In my experience with financial systems it's reasonable normal that the API returns the way the amount is booked. Normally one would use the sign, but in financial systems the (D)ebit or (C)redit is the better way.
The API is very likely the API of a financial system. If that is true, then I'm missing the amount. Are you sure you've the complete description?
Notice that the first parameter is numeric. You're not in luck. The iSeries and RPG are not very forgiving about the type of a numeric. One can choose from Bit, Zoned, Packed, Decimal, Integer, Float and so on. If the RPG is really RPG instead of ILE RPG, then you can bring that down to Zoned, Packed and Byte.
I assume you've access to the iSeries. Then you can watch the program call, debug information and dump information. That will help you if you have to do "trial and error". If you don't have access, the road will be very hard. You'll receive an error in your java class if the program call is not succesfull. But it will be hard to identify the real error without the information from the iSeries yourself. Therefore, access is really required.
Your sample is mostly on the right track. But your branch parameter is numeric. So you should use AS400ZonedDecimal instead of AS400Text:
AS400ZonedDecimal branchNbr = new AS400ZonedDecimal(7,0)
The RPG program may be expecting packed instead of zoned. No big deal, just use AS400PackedDecimal instead.
As you construct your ProgramParameter object, your constructor requirements are different depending on if they are input or output parameters to your program. For input parameters, just pass the toBytes() results. There is no need to include the length. For output-only parameters, just pass the length.
I agree with Robert's answer that there is some missing information, but his assumptions on the outputness of the error code seems valid. I would guess, however, that the DCIndicator parameter is input since your sample passes a value. For the error code parameter, after your program call, you'll need to extract the value and do something with it. Given what you have already, here is how the program call would work. Take note that I specified a library name of "MyLibrary". That is for example purposes. You will have to determine which library your program object is in.
ProgramCall pgm = new ProgramCall(as400, QSYSObjectPathName.toPath("MyLibrary","ZM30000R","PGM"), parmList);
if (pgm.run() == true) {
String sErrorCode = (String) errorCodeTxt.toObject(parmList[3].getOutputData());
//Do something with your output data.
}
else {
AS400Message[] messageList = pgm.getMessageList();
for (int i=0; i<messageList.length; i++) {
String sMessageID = messageList[i].getID();
String sMessageText = messageList[i].getText();
//Do something with the error messages
}
}
Something else to consider is library lists. Does the RPG program expect certain libraries to be in the library list? If so, you should issue CommandCalls to add the libraries to the library list before calling the program.
FWIW: It's a lot easier to call IBM i host programs & service programs using PCML rather than ProgramCall.
The compilers will even generate the PCML document for you.
See http://javadoc.midrange.com/jtopen/com/ibm/as400/data/ProgramCallDocument.html for details.
If you don't have connectivity, then you really can't do what is asked. How do you test it? Is there numeric parameters or are they all character?

Categories