Skip to content

Commit

Permalink
Improve BSTR Handling
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiasblaesing committed Apr 11, 2016
1 parent faba63d commit 45202a1
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Bug Fixes
* [#601](https://github.com/java-native-access/jna/pull/601): Remove COMThread and COM initialization from objects and require callers to initialize COM themselves. Asserts are added to guard correct usage. - [@matthiasblaesing](https://github.com/matthiasblaesing).
* [#602](https://github.com/java-native-access/jna/pull/602): Make sure SID related memory is properly released once no longer required [@lgoldstein](https://github.com/lgoldstein).
* [#610](https://github.com/java-native-access/jna/pull/610): Fixed issue #604: Kernel32#GetLastError() always returns ERROR_SUCCESS [@lgoldstein](https://github.com/lgoldstein).
* [#634](https://github.com/java-native-access/jna/pull/634): Improve BSTR handling - [@matthiasblaesing](https://github.com/matthiasblaesing).

Release 4.2.1
=============
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
package com.sun.jna.platform.win32.COM;

import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WTypes.BSTRByReference;
import com.sun.jna.platform.win32.WinNT.HRESULT;


/**
Expand Down Expand Up @@ -81,7 +79,7 @@ public interface IMoniker extends IPersistStream {
*
* @see <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms680754%28v=vs.85%29.aspx">MSDN</a>
*/
HRESULT GetDisplayName(Pointer pbc, Pointer pmkToLeft, BSTRByReference ppszDisplayName);
String GetDisplayName(Pointer bindContext, Pointer pmkToLeft);

void ParseDisplayName();

Expand Down
24 changes: 19 additions & 5 deletions contrib/platform/src/com/sun/jna/platform/win32/COM/Moniker.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@

import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WTypes.BSTRByReference;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.platform.win32.Guid.CLSID;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.Ole32;
import com.sun.jna.platform.win32.WTypes;
import com.sun.jna.ptr.PointerByReference;

public class Moniker extends Unknown implements IMoniker {

Expand Down Expand Up @@ -119,13 +120,26 @@ public void RelativePathTo() {
}

@Override
public HRESULT GetDisplayName(Pointer pbc, Pointer pmkToLeft, BSTRByReference ppszDisplayName) {
public String GetDisplayName(Pointer pbc, Pointer pmkToLeft) {
final int vTableId = vTableIdStart + 13;

PointerByReference ppszDisplayNameRef = new PointerByReference();

WinNT.HRESULT hr = (WinNT.HRESULT) this._invokeNativeObject(vTableId, new Object[] { this.getPointer(), pbc,
pmkToLeft, ppszDisplayName }, WinNT.HRESULT.class);
pmkToLeft, ppszDisplayNameRef }, WinNT.HRESULT.class);

return hr;
COMUtils.checkRC(hr);

Pointer ppszDisplayName = ppszDisplayNameRef.getValue();
if(ppszDisplayName == null) {
return null;
}

WTypes.LPOLESTR oleStr = new WTypes.LPOLESTR(ppszDisplayName);
String name = oleStr.getValue();
Ole32.INSTANCE.CoTaskMemFree(ppszDisplayName);

return name;
}

@Override
Expand Down
16 changes: 16 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/OleAuto.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,22 @@ public interface OleAuto extends StdCallLibrary {
*/
void SysFreeString(BSTR bstr);

/**
* Returns the length (in bytes) of a BSTR.
*
* @param bstr
* Unicode string that was allocated previously.
*/
int SysStringByteLen(BSTR bstr);

/**
* Returns the length of a BSTR.
*
* @param bstr
* Unicode string that was allocated previously.
*/
int SysStringLen(BSTR bstr);

/**
* The VariantInit function initializes the VARIANTARG by setting the vt
* field to VT_EMPTY. Unlike VariantClear, this function does not interpret
Expand Down
66 changes: 57 additions & 9 deletions contrib/platform/src/com/sun/jna/platform/win32/WTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef.USHORT;
import com.sun.jna.ptr.ByReference;
import java.io.UnsupportedEncodingException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Constant defined in WTypes.h
Expand Down Expand Up @@ -61,35 +64,80 @@ public interface WTypes {
public static int CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER
| CLSCTX_LOCAL_SERVER;

/**
* BSTR wrapper.
*
* <p>From MSDN:</p>
*
* <blockquote>A BSTR (Basic string or binary string) is a string data type
* that is used by COM, Automation, and Interop functions. Use the BSTR data
* type in all interfaces that will be accessed from script.</blockquote>
*
* <p>The memory structure:</p>
*
* <dl>
* <dt>Length prefix</dt>
* <dd>Length of the data array holding the string data and does not include
* the final two NULL characters.</dd>
* <dt>Data string</dt>
* <dd>UTF-16LE encoded bytes for the string.</dd>
* <dt>Terminator</dt>
* <dd>Two null characters</dd>
* </dl>
*
* <p>The "value" of the BSTR is the pointer to the start of the Data String,
* the length prefix is the four bytes before that.</p>
*
* <p>The MSDN states, that a BSTR derived from a Nullpointer is treated
* as a string containing zero characters.</p>
*/
public static class BSTR extends PointerType {
public static class ByReference extends BSTR implements
Structure.ByReference {
}

public BSTR() {
super(new Memory(Pointer.SIZE));
super(Pointer.NULL);
}

public BSTR(Pointer pointer) {
super(pointer);
}

public BSTR(String value) {
super(new Memory((value.length() + 1L) * Native.WCHAR_SIZE));
super();
this.setValue(value);
}

public void setValue(String value) {
this.getPointer().setWideString(0, value);
if(value == null) {
value = "";
}
try {
byte[] encodedValue = value.getBytes("UTF-16LE");
// 4 bytes for the length prefix, length for the encoded data,
// 2 bytes for the two NULL terminators
Memory mem = new Memory(4 + encodedValue.length + 2);
mem.clear();
mem.setInt(0, encodedValue.length);
mem.write(4, encodedValue, 0, encodedValue.length);
this.setPointer(mem.share(4));
} catch (UnsupportedEncodingException ex) {
throw new RuntimeException("UTF-16LE charset is not supported", ex);
}
}

public String getValue() {
Pointer pointer = this.getPointer();
String str = null;
if (pointer != null)
str = pointer.getWideString(0);

return str;
try {
Pointer pointer = this.getPointer();
if(pointer == null) {
return "";
}
int stringLength = pointer.getInt(-4);
return new String(pointer.getByteArray(0, stringLength), "UTF-16LE");
} catch (UnsupportedEncodingException ex) {
throw new RuntimeException("UTF-16LE charset is not supported", ex);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import com.sun.jna.platform.win32.Ole32;
import com.sun.jna.platform.win32.Guid.REFIID;
import com.sun.jna.platform.win32.WTypes;
import com.sun.jna.platform.win32.WTypes.BSTRByReference;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.ULONG;
Expand Down Expand Up @@ -114,13 +115,8 @@ public void GetObject() {

PointerByReference ppbc = new PointerByReference();
Ole32.INSTANCE.CreateBindCtx(new DWORD(), ppbc);
//IBindCtx pbc = new BindCtx(ppbc.getValue());

BSTRByReference ppszDisplayName = new BSTRByReference();
hr = moniker.GetDisplayName(ppbc.getValue(), moniker.getPointer(), ppszDisplayName);
COMUtils.checkRC(hr);
String name = ppszDisplayName.getString();
Ole32.INSTANCE.CoTaskMemFree(ppszDisplayName.getPointer().getPointer(0));

String name = moniker.GetDisplayName(ppbc.getValue(), moniker.getPointer());

PointerByReference ppunkObject = new PointerByReference();
hr = rot.GetObject(moniker.getPointer(), ppunkObject);
Expand Down
39 changes: 39 additions & 0 deletions contrib/platform/test/com/sun/jna/platform/win32/WTypesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WTypes.BSTR;
import junit.framework.TestCase;

public class WTypesTest extends TestCase {
Expand Down Expand Up @@ -53,7 +54,45 @@ public void testLPWSTRConstruction() {
WTypes.LPWSTR fromPointer = new WTypes.LPWSTR(TEST_POINTER);
assertEquals(fromPointer.getValue(), TEST_STRING);
}

public void testBSTRBasic() {
String demoString = "input\u00D6\u00E4\u00DC?!";
// Allocation via system and the "correct" way
BSTR sysAllocated = OleAuto.INSTANCE.SysAllocString(demoString);
// Java based allocation - not suitable if passed via automation
BSTR javaAllocated = new BSTR(demoString);

// Ensure encoding roundtripping works
assertEquals(demoString, sysAllocated.getValue());
assertEquals(demoString, javaAllocated.getValue());

// BSTR is encoded as UTF-16/UCS2, so byte length is 2 * char count
assertEquals(demoString.length(), OleAuto.INSTANCE.SysStringLen(sysAllocated));
assertEquals(demoString.length(), OleAuto.INSTANCE.SysStringLen(javaAllocated));
assertEquals(2 * demoString.length(), OleAuto.INSTANCE.SysStringByteLen(sysAllocated));
assertEquals(2 * demoString.length(), OleAuto.INSTANCE.SysStringByteLen(javaAllocated));

// The BSTR Pointer points 4 bytes into the data itself (beginning of data
// string, the 4 preceding bytes code the string length (in bytes)
assertEquals(2 * demoString.length(), sysAllocated.getPointer().getInt(-4));
assertEquals(2 * demoString.length(), javaAllocated.getPointer().getInt(-4));

OleAuto.INSTANCE.SysFreeString(sysAllocated);
// javaAllocated is allocated via Memory and will be freeed by the
// garbadge collector automaticly
}

public void testBSTRNullPointerHandling() {
// Allocation from NULL should return NULL
BSTR sysAllocated = OleAuto.INSTANCE.SysAllocString(null);
assertNull(sysAllocated);

// MSDN states, that the BSTR from Nullpointer represents the string with
// zero characters
BSTR bstr = new BSTR(Pointer.NULL);
assertEquals("", bstr.getValue());
}

public static void main(String[] args) {
junit.textui.TestRunner.run(WTypesTest.class);
}
Expand Down

0 comments on commit 45202a1

Please sign in to comment.