I am trying to isolate an issue I have when I am calling the MS AutomationClient COM control from JNA.
I have created a handler for the overall library :
public static UIAutomationHandler create() {
Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_APARTMENTTHREADED);
PointerByReference pbr = new PointerByReference();
WinNT.HRESULT hr = Ole32.INSTANCE.CoCreateInstance(
CLSID_CUIAutomation,
null,
WTypes.CLSCTX_SERVER,
IID_IUIAutomation,
pbr);
COMUtils.checkRC(hr);
UIAutomationHandler tb = new UIAutomationHandler(pbr.getValue());
return tb;
}
and I have written methods that call the COM methods (this is an example that works):
public void GetRootElement(PointerByReference elt) {
int result = this._invokeNativeInt(5, new Object[]{this.getPointer(), elt});
COMUtils.checkRC(new WinNT.HRESULT(result));
}
When I call the CreateAndCondition method, which takes 2 other properties, and gives back another property, which looks as follows:
public void CreateAndCondition(Pointer condition0, Pointer condition1, PointerByReference condition) {
int result = this._invokeNativeInt(25, new Object[]{this.getPointer(), condition0, condition1, condition});
COMUtils.checkRC(new WinNT.HRESULT(result));
}
The following code has been extracted and simplified as much as I can ..
PointerByReference pbr0 = new PointerByReference();
PointerByReference pbr1 = new PointerByReference();
PointerByReference pbr = new PointerByReference();
Variant.VARIANT var1 = new Variant.VARIANT.ByReference();
Variant.VARIANT var2 = new Variant.VARIANT.ByReference();
var2.setValue(Variant.VT_INT, ControlType.Window);
var1.setValue(Variant.VT_BSTR, sysAllocated);
this.handler.CreatePropertyCondition(PropertyID.Name.getValue(), var1, pbr0);
this.handler.CreatePropertyCondition(PropertyID.ControlType.getValue(), var2, pbr1);
this.handler.CreateAndCondition(pbr0.getPointer(), pbr1.getPointer(), pbr);
I get the following stack trace:
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:386)
at com.sun.jna.Function.invoke(Function.java:321)
at com.sun.jna.Function.invoke(Function.java:276)
at com.sun.jna.Function.invoke(Function.java:267)
at com.sun.jna.Function.invokeInt(Function.java:674)
at com.sun.jna.platform.win32.COM.COMInvoker._invokeNativeInt(COMInvoker.java:27)
at mmarquee.automation.uiautomation.impl.UIAutomationHandler.CreateAndCondition(UIAutomationHandler.java:82)
at mmarquee.automation.UIAutomation.getDesktopWindow(UIAutomation.java:205)
at mmarquee.automation.TestMainWPF.run(TestMainWPF.java:57)
at mmarquee.automation.MainWPF.main(MainWPF.java:23)
I have written versions of this code for Delphi and an old version that used the com4jna library, but this seems to have me defeated.
So what am I doing wrong ?
Thanks in advance
UPDATE: I believe that the problem is actually in the definition of the GetPropertyCondition, which takes a Variant (as below).
public void CreatePropertyCondition(int propertyId, Variant.VARIANT value, PointerByReference elt) {
int result = this._invokeNativeInt(UIA_CREATE_PROPERTY_CONDITION, new Object[]{this.getPointer(), propertyId, value, elt});
COMUtils.checkRC(new WinNT.HRESULT(result));
}
When I QueryInterface on the returned object, then I get the same error. So it something to do with marshalling the variant via COM.
Several things were wrong, but the calling code should look more like the following:
Variant.VARIANT.ByValue var1 = new Variant.VARIANT.ByValue();
Variant.VARIANT.ByValue var2 = new Variant.VARIANT.ByValue();
var2.setValue(Variant.VT_INT, ControlType.Window);
var1.setValue(Variant.VT_BSTR, sysAllocated);
this.handler.CreatePropertyCondition(PropertyID.Name.getValue(), var1, pbr0);
this.handler.CreatePropertyCondition(PropertyID.ControlType.getValue(), var2, pbr1);
this.handler.CreateAndCondition(pbr0.getValue(), pbr1.getValue(), pbr);
With the Variants being ByValue rather than ByReferece, and input to CreateAndCondition being .getValue(), rather than .getPointer()
Related
I'm using the JNA to call the Windows API. I want to start a process (doesn't matter which) as a specific user. The two API calls I use are:
LogonUserW
CreateProcessAsUserW
LogonUserW succeeds, but CreateProcessAsUserW fails with Error 6. According to the Windows System Error Codes Doc, this corresponds to "ERROR_INVALID_HANDLE".
As far as I can tell, the only handle I pass in is the user handle. I don't see what could be wrong with that. According to the LogonUserW doc,
In most cases, the returned handle is a primary token that you can use in calls to the CreateProcessAsUser function. However, if you specify the LOGON32_LOGON_NETWORK flag, LogonUser returns an impersonation token that you cannot use in CreateProcessAsUser unless you call DuplicateTokenEx to convert it to a primary token.
However, I don't use LOGON32_LOGON_NETWORK.
Some of the struct parameters have handles, but I either pass NULL or they are populated by the API call instead of by me.
Here's the meat of my code:
final PointerByReference userPrimaryToken =
new PointerByReference();
System.out.printf(
"ptr.peer = %d\n",
Pointer.nativeValue(userPrimaryToken.getValue())
);
final boolean logonOk = MyWinBase.INSTANCE.LogonUserW(
toCString(<my-username>), // hidden
toCString("ANT"),
toCString(<my-password>), // hidden
/* This logon type is intended for batch servers, where
processes may be executing on behalf of a user without their
direct intervention. This type is also for higher
performance servers that process many plaintext
authentication attempts at a time, such as mail or web
servers.*/
WinBase.LOGON32_LOGON_BATCH,
WinBase.LOGON32_PROVIDER_DEFAULT,
userPrimaryToken
);
System.out.printf("ok = %b\n", logonOk);
System.out.printf(
"ptr.peer = %d\n",
Pointer.nativeValue(userPrimaryToken.getValue())
);
final STARTUPINFOW.ByReference startupInfoW =
new STARTUPINFOW.ByReference();
startupInfoW.cb = startupInfoW.size();
startupInfoW.lpReserved = Pointer.NULL;
startupInfoW.lpDesktop = Pointer.NULL;
startupInfoW.lpTitle = Pointer.NULL;
startupInfoW.dwFlags
= startupInfoW.dwX = startupInfoW.dwY
= startupInfoW.dwXSize = startupInfoW.dwYSize
= startupInfoW.dwXCountChars = startupInfoW.dwYCountChars
= startupInfoW.dwFillAttribute
= startupInfoW.wShowWindow
= 0;
startupInfoW.cbReserved2 = 0;
startupInfoW.lpReserved2 = Pointer.NULL;
startupInfoW.hStdInput = startupInfoW.hStdOutput
= startupInfoW.hStdError
= Pointer.NULL;
final PROCESS_INFORMATION.ByReference processInformation =
new PROCESS_INFORMATION.ByReference();
processInformation.hProcess = processInformation.hThread
= Pointer.NULL;
processInformation.dwProcessId = processInformation.dwThreadId
= 0;
final boolean createProcessOk = MyProcessThreadsApi.INSTANCE
.CreateProcessAsUserW(
userPrimaryToken.getPointer(),
toCString("C:\\Windows\\System32\\cmd.exe"),
// execute and terminate
toCString("/c whoami > whoami.txt"),
Pointer.NULL,
Pointer.NULL,
false,
WinBase.CREATE_UNICODE_ENVIRONMENT,
new PointerByReference(),
Pointer.NULL,
startupInfoW,
processInformation
);
System.out.printf("ok = %b\n", createProcessOk);
System.out.printf(
"dwProcessId = %d\n", processInformation.dwProcessId
);
System.out.printf(
"last err code = %d\n",
ErrHandlingApi.INSTANCE.GetLastError()
);
Here's my output:
ptr.peer = 0
ok = true
ptr.peer = 1040
ok = false
dwProcessId = 0
last err code = 6
Any suggestions?
Looking at this piece of code:
final PointerByReference userPrimaryToken = ...;
Online documentation says it represents a pointer to pointer, C notation void**
https://java-native-access.github.io/jna/4.2.1/com/sun/jna/ptr/PointerByReference.html
On documentation for LogonUser it expects a PHALDLE pointer to a HANDLE, which is similar to a pointer to pointer, since HANDLE is similar to pointer (it is declared as typedef void *HANDLE;).
https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonuserw
BOOL LogonUserW(
....
DWORD dwLogonProvider,
PHANDLE phToken
);
But in documentation to CreateProcessAsUser is specified that this function accepts a HANDLE, not a PHANDLE
https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasuserw
BOOL CreateProcessAsUserW(
HANDLE hToken,
LPCWSTR lpApplicationName,
....
);
So I would expect you to pass rather getValue than getPointer. By using getPointer you get the pointer itself, which is most probably the pointer to pointer in your case. I don't know JNA, but expectances are from knowledge of WinAPI
final boolean createProcessOk = MyProcessThreadsApi.INSTANCE
.CreateProcessAsUserW(
userPrimaryToken.getValue(),
....
);
I've come across an issue with LuaJ not accepting an LuaValue as an argument when the Java code specifically asks for an LuaValue.
public void registerEvent(LuaValue id, String event, String priority,
LuaValue callback)
{
if(!(id instanceof LuaTable))
{
throw new RuntimeException("id must be an LuaTable");
}
EventDispatcher.addHandler(id, event, priority, callback);
}
Ideally, this would allow the code in Lua to simply read like so...
function main(this)
this.modName="Some Mod"
this.lastX = 0
hg.both.registerEvent(this, "inputcapturedevent", "last", eventRun)
end
function eventRun(this, event)
this.lastX += event.getX()
end
Sadly, this simple gives an error that it expects userdata, but got a table.
org.luaj.vm2.LuaError: script:4 bad argument: userdata expected, got table
The value of "this" is the same LuaTable in both cases, but because the method registerEvent is added via CoerceJavaToLua.coerce(...) it believes it wants a java Object instead of realising it really wants an LuaVale.
So my question is this. Is there a better way around this that allows me to use the same function from both Java and Lua? And thanks for your time if you read it all the way here :)
The error you are getting is probably a red herring and may be due to the way you are binding the "registerEvent" function into the value of "hg.both". Possibly you just need to use the method syntax instead, such as
hg.both:registerEvent(this, "inputcapturedevent", "last", eventRun)
If you want to use the dot syntax hg.both.registerEvent, then using VarArgFunction and implementing invoke() may be a more direct way to implement this. In this example, Both.registerEvent is a plain variable that is a VarArgFunction.
public static class Both {
public static VarArgFunction registerEvent = new VarArgFunction() {
public Varargs invoke(Varargs args) {
LuaTable id = args.checktable(1);
String event = args.tojstring(2);
String priority = args.tojstring(3);
LuaValue callback = args.arg(4);
EventDispatcher.addHandler(id, event, priority, callback);
return NIL;
}
};
}
public static void main(String[] args) throws ScriptException {
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine engine = sem.getEngineByName("luaj");
Bindings sb = engine.createBindings();
String fr =
"function main(this);" +
" this.modName='Some Mod';" +
" this.lastX = 0;" +
" hg.both.registerEvent(this, 'inputcapturedevent', 'last', eventRun);" +
"end;";
System.out.println(fr);
CompiledScript script = ((Compilable) engine).compile(fr);
script.eval(sb);
LuaFunction mainFunc = (LuaFunction) sb.get("main");
LuaTable hg = new LuaTable();
hg.set("both", CoerceJavaToLua.coerce(Both.class));
sb.put("hg", hg);
LuaTable library = new LuaTable();
mainFunc.call(CoerceJavaToLua.coerce(library));
}
Ok I'll try and keep this short.
First let me explain exactly what I am trying to get. If you open Windows Explorer and go to a network drive there is a DFS tab there(must have DFS enabled VIA the servers on the network so it may not be there).
In that tab there is a list called the "Referral List"... I want what is in that box. I believe this is the DFS or UNC, you can correct me it will help me.
What I have is the \domainname.com\something$\BUS\blah\myDriveHome but this is tied to something else in that box that contains the actual server that that share is setting on and that share is what I need to run a compliance check.
I cannot use an exe that is not package with Windows 7 not any other exe as we cannot distribute exes.
So what have I done... a VERY thorough search for things like DFS/UNC paths from the command line, powershell, and registry and no go. Command line "net use" only return the linked path and not the server so that is useless.
I only ever post a question when I hit a wall that is taking up to much programming time.
If anyone has an info it would be grateful.
Thanks
I was able to steal the C# code in this answer here and make some modifications so it works with .Net 2.0, and use it within PowerShell:
$dfsCode = #'
using System;
using System.Runtime.InteropServices;
public static class Dfs
{
private enum NetDfsInfoLevel
{
DfsInfo1 = 1,
DfsInfo2 = 2,
DfsInfo3 = 3,
DfsInfo4 = 4,
DfsInfo5 = 5,
DfsInfo6 = 6,
DfsInfo7 = 7,
DfsInfo8 = 8,
DfsInfo9 = 9,
DfsInfo50 = 50,
DfsInfo100 = 100,
DfsInfo150 = 150,
}
[DllImport("netapi32.dll", SetLastError = true)]
private static extern int NetApiBufferFree(IntPtr buffer);
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int NetDfsGetInfo(
[MarshalAs(UnmanagedType.LPWStr)] string DfsEntryPath, // DFS entry path for the volume
[MarshalAs(UnmanagedType.LPWStr)] string ServerName, // This parameter is currently ignored and should be NULL
[MarshalAs(UnmanagedType.LPWStr)] string ShareName, // This parameter is currently ignored and should be NULL.
NetDfsInfoLevel Level, // Level of information requested
out IntPtr Buffer // API allocates and returns buffer with requested info
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct DFS_INFO_3
{
[MarshalAs(UnmanagedType.LPWStr)]
public string EntryPath;
[MarshalAs(UnmanagedType.LPWStr)]
public string Comment;
public int State;
public int NumberOfStorages;
public IntPtr Storage;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct DFS_STORAGE_INFO
{
public int State;
[MarshalAs(UnmanagedType.LPWStr)]
public string ServerName;
[MarshalAs(UnmanagedType.LPWStr)]
public string ShareName;
}
private static T GetStruct<T>(IntPtr buffer, int offset)where T:struct
{
T r = new T();
r = (T) Marshal.PtrToStructure((IntPtr)((long)buffer + offset * Marshal.SizeOf(r)), typeof(T));
return r;
}
public static string GetDfsInfo(string server)
{
string rval = null;
IntPtr b;
int r = NetDfsGetInfo(server, null, null, NetDfsInfoLevel.DfsInfo3, out b);
if(r != 0)
{
NetApiBufferFree(b);
// return passed string if not DFS
return rval;
}
DFS_INFO_3 sRes = GetStruct<DFS_INFO_3>(b,0);
if(sRes.NumberOfStorages > 0)
{
DFS_STORAGE_INFO sResInfo = GetStruct<DFS_STORAGE_INFO>(sRes.Storage,0);
rval = string.Concat(#"\\", sResInfo.ServerName, #"\", sResInfo.ShareName, #"\");
}
NetApiBufferFree(b);
return rval;
}
}
'#
Add-Type -TypeDefinition $dfsCode
[Dfs]::GetDfsInfo('\\ad.domain.com\Share')
This code will work with PowerShell 2.0 which is included with Windows 7.
I went another direction with the use of PSEXEC and DFSUtil to find the DFS info VIA the remote PC. Returns a lot of info but I filtered it in PowerShell after reading the file and matching the UNC. I would post the how to but I had to do some major adapting on my end with the info that is on a few other sites for DFSUtil and what to look for and PSExec. I will note this for PSEXEC:
cmd.exe /s /c C:\Temp\psexec.exe 2> $null
That "2> $null" will save you some headaches and your script crashing if the return is in the error channel. You will need to run it in the PS console though without that to catch the error, but when you have a script like mine performing 50+ system checks you don't want the whole thing to halt for just one error.
I'm using android java plugin to call my java function in unity like this:
static IntPtr cls_Activity;
static IntPtr fid_Activity;
static IntPtr obj_Activity;
static IntPtr kdataActivityClass;
static IntPtr startAdsMethod;
void Start () {
cls_Activity = AndroidJNI.FindClass("com/unity3d/player/UnityPlayer");
fid_Activity = AndroidJNI.GetStaticFieldID(cls_Activity, "currentActivity", "Landroid/app/Activity;");
obj_Activity = AndroidJNI.GetStaticObjectField(cls_Activity, fid_Activity);
kdataActivityClass = AndroidJNI.FindClass("com/kdata/unitytest/UnityUrlPlugin");
startAdsMethod = AndroidJNI.GetMethodID(PakdataActivityClass, "getURL","()V");
Debug.Log("obj_Activity"+obj_Activity);
Debug.Log("kdataActivityClass"+kdataActivityClass);
Debug.Log("Method"+startAdsMethod);
if (AndroidJNI.IsInstanceOf(obj_Activity, kdataActivityClass) != false)
{
jvalue[] myArray = new jvalue[1];
AndroidJNI.CallStaticStringMethod(obj_Activity, startAdsMethod, myArray);
}
The problem is that I am getting access to the class but method from the class is returns null
Debug.Log("obj_Activity"+obj_Activity); =>retuns value
Debug.Log("kdataActivityClass"+kdataActivityClass); =>returns =>value
Debug.Log("Method"+startAdsMethod); =>retunns null <=== here is the problem this method
Should return a hardcoded string but its not working in unity.
Help is highly appreciated.
Thanks
Check getURL method jni signature with javap (did you put the same to your third parameter ?) :
cd <pathToUnityUrlPluginClass>
javap -p -s <UnityUrlPlugin>
I'm experimenting with the Java scripting engine and Ruby, and I'm having trouble setting some instance variables in a ruby script. This could be my lack of understanding of Ruby or my lack of understanding of how to use ruby classes in the scripting engine. With the following code:
public class App {
public static void main( String[] args ) throws Exception{
ScriptEngineManager sm = new ScriptEngineManager();
ScriptEngine se = sm.getEngineByName("jruby");
StringBuilder sb = new StringBuilder();
sb.append("class Test\n");
sb.append(" attr_accessor :a, :b\n");
sb.append(" def str\n");
sb.append(" \"#{a}, #{b} is a test.\"\n");
sb.append(" end\n");
sb.append("end\n");
sb.append("o = Test.new\n");
Object o = se.eval(sb.toString());
se.put("#a", "A");
se.put("#b", "B");
System.out.println( ((Invocable) se).invokeMethod(o, "str"));
}
}
I'd expect the output to be 'A, B is a test'
Instead, the output is ', is a test'.
How should I be setting variables a, b in this code?
Edit: Just to be clear, ideally I don't want to be setting the variables by appending them to this StringBuilder - this is just for illustration. In practice, I'll be loading scripts from some source, and then want to set properties and call methods on that Ruby object afterwards. I'm sure I'm just missing some crucial step that everyone else knows about :). Thanks to Gareth Davis' answer I've found I can use bindings and global variables successfully, but that isn't going to work with all scripts. Would really appreciate any links to good articles that go beyond 'hello world' type usage, as I've not found any decent ones.
Second edit: This is the working, final code, with the crucial line that I knew must be missing :)-
public class App {
public static void main( String[] args ) throws Exception{
//Must set this property if you want to call eval multiple times!
System.setProperty("org.jruby.embed.localvariable.behavior", "persistent");
ScriptEngineManager sm = new ScriptEngineManager();
ScriptEngine se = sm.getEngineByName("jruby");
StringBuilder sb = new StringBuilder();
sb.append("class Test\n");
sb.append(" attr_accessor :a, :b\n");
sb.append(" def str\n");
sb.append(" \"#{a}, #{b} is a test.\"\n");
sb.append(" end\n");
sb.append("end\n");
sb.append("o = Test.new\n");
Object o = se.eval(sb.toString());
se.eval("o.a = \"A\"");
se.eval("o.b = \"B\"");
System.out.println( ((Invocable) se).invokeMethod(o, "str"));
}
}
That won't work like that. The only way to set the values of a & b is to evaluate o.a = 'A' and o.b = 'B'.
The first solution is to amend the script to populate the values thus:
sb.append("o.a = 'A'\n");
sb.append("o.b = 'B'\n");
sb.append("o");
Object o = se.eval(sb.toString());
I've created a working example on github.com
keeping with the question the following can be used (credit to #Mick Sear):
System.setProperty("org.jruby.embed.localvariable.behavior", "persistent");
// .. snip
Object o = se.eval(sb.toString());
se.eval("o.a = 'A'");
se.eval("o.b = 'B'");
System.out.println( ((Invocable) se).invokeMethod(o, "str"));