I would like to use the MMDevice API from my Java app. What are my options?
I tried to use JNA. Looks like I can't use JNA Typelib parsing because there no types for this API (Is there a COM type library for Windows Core Audio). As suggested, I need to provide my own declarations of the API.
So I also tried both JNA examples with manual declarations but they give "Interface not supported HRESULT=80004002" error:
public class MMDeviceAPITest {
public static void test1() {
try {
Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED);
var obj = new Test1.MMDeviceEnumerator(); // exception E_NOINTERFACE (HRESULT: 80004002)
// ...
} finally {
Ole32.INSTANCE.CoUninitialize();
}
}
public static void test2() {
try {
Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED);
var factory = new Factory();
var obj = factory.createObject(Test2.MMDeviceEnumerator.class); // exception E_NOINTERFACE (HRESULT: 80004002)
var in = obj.queryInterface(Test2.IMMDeviceEnumerator.class);
// ...
} finally {
Ole32.INSTANCE.CoUninitialize();
}
}
}
interface Test1 {
class MMDeviceEnumerator extends COMLateBindingObject {
public MMDeviceEnumerator() {
super(new Guid.CLSID("bcde0395-e52f-467c-8e3d-c4579291692e"), true);
}
}
}
interface Test2 {
#ComObject(clsId = "bcde0395-e52f-467c-8e3d-c4579291692e")
interface MMDeviceEnumerator extends IUnknown {} // doesn't extend IUnknown in C sources, probably it's the problem...
#ComInterface(iid = "a95664d2-9614-4f35-a746-de8db63617e6")
interface IMMDeviceEnumerator extends IUnknown {}
}
Any ideas how I could access this API from Java? Can I somehow create working declarations for JNA? Or use another framework maybe?
My last idea is to create/find a micro native app/library that wraps the needed COM calls, so I could call this app/library easily (via subprocesses or simple JNA declarations). I'm new to COM world, but it sounds working for me...
The docs you linked show how to create using CoCreateInstance:
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
This should get you somewhere close with JNA.
class MMDeviceEnumerator extends Unknown {
public static final CLSID CLSID_MMDeviceEnumerator = new CLSID("bcde0395-e52f-467c-8e3d-c4579291692e");
public static final GUID IID_IMMDeviceEnumerator = new GUID("a95664d2-9614-4f35-a746-de8db63617e6");
public MMDeviceEnumerator(Pointer p) {
super(p);
}
public static MMDeviceEnumerator create() {
PointerByReference pEnumerator = new PointerByReference();
HRESULT hres = Ole32.INSTANCE.CoCreateInstance(
CLSID_MMDeviceEnumerator, null,
WTypes.CLSCTX_ALL, IID_IMMDeviceEnumerator,
pEnumerator);
if (COMUtils.FAILED(hres)) {
return null;
}
return new MMDeviceEnumerator(pEnumerator.getValue());
}
// map functions as needed
}
I used the implementation of IWbemContext in JNA as a template above. You can consult that class for example COM function mappings.
For some reason I can't suggest edits to the answer of Daniel Widdis. The answer worked for me, many thanks! Just wanted to show how to map one method as an example:
class MMDeviceEnumerator extends Unknown {
public static final CLSID CLSID_MMDeviceEnumerator = new CLSID("bcde0395-e52f-467c-8e3d-c4579291692e");
public static final GUID IID_IMMDeviceEnumerator = new GUID("a95664d2-9614-4f35-a746-de8db63617e6");
public MMDeviceEnumerator(Pointer p) {
super(p);
}
public static MMDeviceEnumerator create() {
PointerByReference pEnumerator = new PointerByReference();
HRESULT hres = Ole32.INSTANCE.CoCreateInstance(
CLSID_MMDeviceEnumerator, null,
WTypes.CLSCTX_ALL, IID_IMMDeviceEnumerator, pEnumerator);
if (COMUtils.FAILED(hres)) {
return null;
}
return new MMDeviceEnumerator(pEnumerator.getValue());
}
public static final int EDataFlow_eRender = 0;
public static final int EDataFlow_eCapture = 1;
public static final int EDataFlow_eAll = 2;
public static final int EDataFlow_enum_count = 3;
public static final int DEVICE_STATE_ACTIVE = 0x1;
public static final int DEVICE_STATE_DISABLED = 0x2;
public static final int DEVICE_STATE_NOTPRESENT = 0x4;
public static final int DEVICE_STATE_UNPLUGGED = 0x8;
public static final int DEVICE_STATEMASK_ALL = 0xF;
public void EnumAudioEndpoints(int dataFlow, int dwStateMask, PointerByReference ppDevices) {
WinNT.HRESULT res = (WinNT.HRESULT) _invokeNativeObject(
3, // `EnumAudioEndpoints` is the 3rd method of `IMMDeviceEnumeratorVtbl` in `mmdeviceapi.h`
new Object[] { getPointer(), dataFlow, new WinDef.DWORD(dwStateMask), ppDevices},
WinNT.HRESULT.class
);
COMUtils.checkRC(res);
}
// map other functions as needed
}
Related
I tried to translate the SHGetFileInfo function from Shell32 into Java with JNA and used C# code and this as reference
While in the C# code psfi.iIcon is 432 in my translated Java code psfi.iIcon is 0. If I am right, for the same file they should be same no matter which language I use, shouldn't they?
My Java code:
public static void main(String[] args) {
String path = "[PATH_TO_EXE]\\test.exe"; //Of course in my code I used the real path
SHFILEINFO sfi = new SHFILEINFO();
DWORD_PTR i = Shell32.INSTANCE.SHGetFileInfo(path, 0, sfi, sfi.size(), SHGFI.SysIconIndex);
System.out.println(sfi.iIcon); //<-- Prints 0, should print 432
}
public static class SHGFI {
static final int SysIconIndex = 0x000004000;
static final int LargeIcon = 0x000000000;
static final int UseFileAttributes = 0x000000010;
}
public interface Shell32 extends StdCallLibrary {
Shell32 INSTANCE = Native.loadLibrary("shell32", Shell32.class, W32APIOptions.UNICODE_OPTIONS);
DWORD_PTR SHGetFileInfo(String pszPath, int dwFileAttributes, SHFILEINFO psfi, int cbFileInfo, int uFlags);
}
public static class SHFILEINFO extends Structure {
public HICON hIcon;
public int iIcon;
public DWORD dwAttributes;
public char[] szDisplayName = new char[260];
public char[] szTypeName = new char[80];
#Override
protected List<String> getFieldOrder() {
return Arrays.asList("hIcon", "iIcon", "dwAttributes", "szDisplayName", "szTypeName");
}
}
Is there anything fundemental that I did wrong? I'm new to JNA and Windows functions
Under the Remarks section, there is this piece of information, which imho might be the source of your problem
You must initialize Component Object Model (COM) with CoInitialize or
OleInitialize prior to calling SHGetFileInfo.
It's a pretty straightforward call
CoInitialize(null);
As DanielWiddis pointed out in the comments, per documentation
New applications should call CoInitializeEx instead of CoInitialize
And
To close the COM library gracefully, each successful call to
CoInitialize or CoInitializeEx, including those that return S_FALSE,
must be balanced by a corresponding call to CoUninitialize
Example
CoInitializeEx(null, 0);
CoUninitialize();
I've searched the internet for something to similar to what I'm doing, but haven't found anything. I'm using interfaces in Java 8 to create a Builder pattern, like so:
public class UrlImmutable {
public final String parentUrl;
public final Double parentUrlSentiment;
public final Set<String> childUrls;
public final boolean isParentVendorUrl;
public final Map<TagClassification, Set<String>> parentUrlArticleTags;
private UrlImmutable(String parentUrl, Double parentUrlSentiment, Set<String> childUrls, boolean isParentVendorUrl,
Map<TagClassification, Set<String>> parentUrlArticleTags ) {
super();
this.parentUrl = parentUrl;
this.parentUrlSentiment = parentUrlSentiment;
this.childUrls = childUrls;
this.isParentVendorUrl = isParentVendorUrl;
this.parentUrlArticleTags = parentUrlArticleTags;
}
/** Our Interfaces for the Builder **/
public interface ParentUrlBuilder {
ParentUrlSentimentBuilder parentUrl(String parentUrl);
}
public interface ParentUrlSentimentBuilder {
ChildUrlBuilder parentUrlSentiment(Double parentUrlSentiment);
}
public interface ChildUrlBuilder {
IsVendorUrlBuilder childUrls(Set<String> childUrls);
}
public interface IsVendorUrlBuilder {
ParentUrlArticleTagsBuilder isParentVendorUrl(boolean isParentVendorUrl);
}
public interface ParentUrlArticleTagsBuilder {
UrlImmutable parentUrlArticleTags(Map<TagClassification,Set<String>> parentUrlArticleTags);
}
public static ParentUrlBuilder discoveredUrl() {
return parentUrl -> parentUrlSentiment -> childUrls -> isParentVendorUrl -> parentUrlArticleTags ->
new UrlImmutable(parentUrl, parentUrlSentiment, childUrls, isParentVendorUrl, parentUrlArticleTags);
}
}
And to construct this object, we do this:
UrlImmutable url =
UrlImmutable()
.parentUrl("http://www.google.com")
.parentUrlSentiment(10.5)
.childUrls(childUrls)
.isParentVendorUrl(true)
.parentUrlArticleTags(parentUrlArticleTags);
I can't seem to find the right combination of annotations for this. Any help much appreciated!
I have classes called ctdl_User, ctdl_Device and ctdl_Options.
I have a function that saves ctdl_User objects using the binary formatter, and another that loads them. However the functions specifically expect to take and return User objects, and I want to use this function to load other objects of mine.
How do I go about changing what types the functions will take? Here is the save and load functions...
public ctdl_User Load()
{
ctdl_User loadedUsr = new ctdl_User();
string DataFileSave = Settings.Default.savePath + "\\testuserfile.dat";
FileStream dataStr = new FileStream(DataFileSave, FileMode.Open);
BinaryFormatter frmtr = new BinaryFormatter();
loadedUsr = (ctdl_User) frmtr.Deserialize(dataStr);
dataStr.Close();
return loadedUsr;
}
public static void Save(ctdl_User usr)
{
string DataFileSave = Settings.Default.savePath + "\\testuserfile.dat";
File.Delete(DataFileSave);
FileStream dataStr = new FileStream(DataFileSave, FileMode.Create);
BinaryFormatter frmtr = new BinaryFormatter();
frmtr.Serialize(dataStr, usr);
dataStr.Close();
}
The following demonstrates an approach using generics in C#:
public static T Load<T>() where T : new()
{
T loadedUsr = new T();
string DataFileSave = Settings.Default.savePath + "\\testuserfile.dat";
FileStream dataStr = new FileStream(DataFileSave, FileMode.Open);
BinaryFormatter frmtr = new BinaryFormatter();
loadedUsr = (T) frmtr.Deserialize(dataStr);
dataStr.Close();
return loadedUsr;
}
public static void Save<T>(T usr)
{
string DataFileSave = Settings.Default.savePath + "\\testuserfile.dat";
File.Delete(DataFileSave);
FileStream dataStr = new FileStream(DataFileSave, FileMode.Create);
BinaryFormatter frmtr = new BinaryFormatter();
frmtr.Serialize(dataStr, usr);
dataStr.Close();
}
Note the use of the new() constraint in the Load() method.
Here's an example of calling these methods:
public static void Main(string[] args)
{
ctdl_User user = new ctdl_User();
user.name = "Alice";
Save<ctdl_User>(user);
ctdl_User user2 = Load<ctdl_User>();
Console.WriteLine(user2.name);
ctdl_Device device = new ctdl_Device();
device.type = "printer";
Save<ctdl_Device>(device);
ctdl_Device device2 = Load<ctdl_Device>();
Console.WriteLine(device2.type);
}
For completeness, here are the stub classes I used to test this code:
[Serializable()]
class ctdl_User
{
public string name;
}
[Serializable()]
class ctdl_Device
{
public string type;
}
Edit: added code to Main() that saves and loads a device, as well as a user.
Assuming this is Java you can just overload the method following this scheme:
public static void Load(Type1 obj)
{
//do sth with object of type Type1
}
public static void Load(Type2 obj)
{
//do sth with object of type Type2
}
//...etc
Also if you have common code for it that can be used for objects of another types you can exrtact this code to one methode that treat overloaded methods as facade
private static void doLoad(Object obj)
{
//the common code
}
public static void Load(Type1 obj)
{
doLoad(obj); // or something else...
}
public static void Load(Type2 obj)
{
doLoad(obj); // or something else...
}
//...etc
You can read more about overloading methods in Java here
I am trying to use ROME to parse an RSS feed like this:
url = new URL("http://www.rssboard.org/files/sample-rss-2.xml");
XmlReader reader = new XmlReader(url);
SyndFeedInput input = new SyndFeedInput();
SyndFeed feed = input.build(reader);
System.out.println(feed.getAuthor());
However, I cannot find a method to get the "WebMaster" field or any other customized field.
I have read about the custom modules in Rome from here, but I couldn't figure out how to use it. I create a similar SamplleModule, SampleModuleImpl, and SampleModule Parser for webMaster field, but I don't know how to use it!
This the classes that I have implemented:
SamplleModule:
public interface SampleModule extends Module {
public static final String URI =
"http://www.rssboard.org/files/sample-rss-2.xml";
public String getWebMaster();
public void setWebMaster(String webMaster);
}
SampleModuleImpl:
public class SampleModuleImpl extends ModuleImpl implements SampleModule {
private static final long serialVersionUID = 1L;
private String _webMaster;
protected SampleModuleImpl() {
super(SampleModule.class, SampleModule.URI);
}
#Override
public void copyFrom(Object obj) {
SampleModule sm = (SampleModule) obj;
setWebMaster(sm.getWebMaster());
}
#Override
public Class getInterface() {
return SampleModule.class;
}
#Override
public String getWebMaster() {
return _webMaster;
}
#Override
public void setWebMaster(String webMaster) {
_webMaster = webMaster;
}
}
and SampleModuleParser:
public class SampleModuleParser implements ModuleParser {
private static final Namespace SAMPLE_NS = Namespace.getNamespace("sample",
SampleModule.URI);
#Override
public String getNamespaceUri() {
return SampleModule.URI;
}
#Override
public Module parse(Element dcRoot) {
boolean foundSomething = false;
SampleModule fm = new SampleModuleImpl();
Element e = dcRoot.getChild("webMaster");
if (e != null) {
foundSomething = true;
fm.setWebMaster(e.getText());
}
return (foundSomething) ? fm : null;
}
}
I have also added these module to rome.properties.
I just don't know how to use them in my reader method.
Any idea folks?
take a look here for an example of how do to this with the MRSS module:
http://ideas-and-code.blogspot.com/2009/07/media-rss-plugin-for-rome-howto.html
Basically you take a SyndEntry object and using the namespace for your module you get an instance of your module object from the entry if one exists, so in your case:
SampleModule myModule = (SampleModule)e.getModule( SampleModule.URI );
And then you can use it. I use groovy with rome for my parser and do things like this:
def mediaModule = entry.getModule("http://search.yahoo.com/mrss/")
if(mediaModule) {
mediaModule.getMediaGroups().each { group ->
group.contents.each { content ->
if(content.type != null && content.type.startsWith("image")) {
log.info "got an image"
String imgUrl = content.getReference().toString()
post.images.add(new MediaContent(type:'image',url:imgUrl))
}
}
}
}
HTH
I found some examples that show me the location of certain method calls using a MethodAdapter:
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(targetClass)
&& name.equals(targetMethod.getName())
&& desc.equals(targetMethod.getDescriptor())) {
callsTarget = true;
}
}
I need the arguments, e.g., if I have object.getText("mykey") I would like to get the text "mykey".
Is this possible ?
I have a framework which you may find useful (specifically, procyon-compilertools). It would enable you to do what you ask in a more object-oriented manner than ASM. However, the project is still early in development and subject to change, so I would not recommend using it in a production project.
You could use this as a starting point:
public class CallInspectionSample {
static class Target {
public static void main(String[] args) {
new Target().getText("MyKey");
}
public String getText(final String key) {
return null;
}
}
public static void main(String[] args) {
final TypeReference targetType = MetadataSystem.instance().lookupType("CallInspectionSample$Target");
final TypeDefinition resolvedType = targetType.resolve();
final MethodDefinition mainMethod = resolvedType.getDeclaredMethods().get(1);
final MethodDefinition getTextMethod = resolvedType.getDeclaredMethods().get(2);
final MethodBody mainBody = mainMethod.getBody();
final Block methodAst = new Block();
final DecompilerContext context = new DecompilerContext();
context.setCurrentType(resolvedType);
context.setCurrentMethod(mainMethod);
methodAst.getBody().addAll(AstBuilder.build(mainBody, true, context));
AstOptimizer.optimize(context, methodAst);
for (final Expression e : methodAst.getChildrenAndSelfRecursive(Expression.class)) {
if (e.getCode() == AstCode.InvokeVirtual &&
((MethodReference) e.getOperand()).resolve() == getTextMethod) {
// Analyze arguments here (skip first for instance methods)...
System.out.println(e.getArguments());
}
}
}
}
(example outputs [initobject:Target(Target::<init>), ldc:String("MyKey")])