📄 comptr.java
字号:
/*
* COMPtr.java -
*
* This file is part of the Jawin Project: http://jawinproject.sourceforge.net/
*
* Please consult the LICENSE file in the project root directory,
* or at the project site before using this software.
*/
/* $Id: COMPtr.java,v 1.5 2004/07/18 14:57:47 arosii_moa Exp $ */
package org.jawin;
import org.jawin.io.NakedByteStream;
import org.jawin.marshal.GenericStub;
import org.jawin.win32.Ole32;
/**
* The base class for all COM vtable or dispatch based interfaces.
* There are certain rules that must be followed to extend this class
* successfully:
* <ul>
* <li>The class MUST have a public no-arg constructor.</li>
* <li>The class MUST register its Interface Identifier (IID) in Jawin. The
* following is the standard way to do this (replace the GUID value with the relevant):
* <pre>
* public static final GUID IID = new GUID("<b>{6EFEB125-55E2-4D6D-A17A-A2F038A647B2}</b>");
* public static final int IID_TOKEN;
* static {
* IID_TOKEN = IdentityManager.registerProxy(IID, <CLASS>.class);
* }
* </pre>
* </li>
* <li>The class MUST implement the abstract method {@link #getIIDToken()} to return
* the registered token for the Interface Identifier. The following is the
* standard way to do this:
* <pre>
* public int getIIDToken() {
* return IID_TOKEN;
* }
* </pre>
* </li>
* <li>For easy access to the interface it is suggested to implement the
* following constructors besides the no-arg constructor:
* <ul>
* <li>For creating a new COM-object with the given progid and with
* a SomeClass interface:
* <pre>
* public SomeClass(String progid) throws COMException {
* super(progid, IID);
* }
* </pre></li>
* <li>For creating a new COM-object with the given CLSID and with
* a SomeClass interface:
* <pre>
* public SomeClass(GUID clsid) throws COMException {
* super(clsid, IID);
* }
* </pre></li>
* <li>For getting the SomeClass interface on an existing COM-object.
* This is an alternative to calling {@link #queryInterface(Class)}
* on the existing COM-object:
* <pre>
* public SomeClass(COMPtr comObject) throws COMException {
* super(comObject);
* }
* </pre></li>
* </ul>
* </li>
* </ul>
*
* <b>Note:</b> Dispatch based interfaces should use {@link DispatchPtr} as
* base class instead of this class.
* <br><br>
* FIXME - the synchronized stuff in this class should be fixed/aligned.
*
* @version $Revision: 1.5 $
* @author Stuart Halloway, http://www.relevancellc.com/halloway/weblog/<br>
* Morten Andersen, arosii_moa (at) users.sourceforge.net
*/
public abstract class COMPtr implements IUnknown {
/**
* The global interface table (GIT) cookie that makes it
* possible to use this COMPtr in multithreaded applications.
* <br><br>
* If the unknown pointer is 0, this MUST be set or the caller
* will get a "NULL GIT peer" error.
*/
private int peer;
/**
* raw, context specific pointer to the vtable for this COMPtr. If
* this is != 0, the GIT cookie is not used. Ie. the caller must
* handle any threading issues himself.
*/
private int unknown;
/**
* for constructing a COMPtr where neither the GIT cookie or
* the vtable pointer has been initialized.
*/
protected COMPtr() {
}
/**
* for constructing a COMPtr, initializing the
* GIT cookie or the vtable pointer at construction
* time. Only one of the parameters should be != 0.
* <br><br>
* Only the IdentityManager creates COMPtrs ? - FIXME what?
*
* @param peer the GIT cookie. Should be set if Jawin
* should handle threading issues automatically. If
* this is not wanted it should be set to 0.
* @param unknown a raw vtable pointer. If set, a COMPtr that can
* ONLY be called from this thread is created. If this is set
* to 0, the GIT cookie must be set.
*/
protected COMPtr(int peer, int unknown) {
this.peer = peer;
this.unknown = unknown;
}
protected COMPtr(String progid, GUID iid) throws COMException {
COMPtr cp = Ole32.GetFromProgID(progid, iid);
// because this is a constructor we can not return cp, but have to "steal" the unknown pointer
// therefore cp.close() are not called
stealUnknown(cp);
}
protected COMPtr(GUID clsid, GUID iid) throws COMException {
//Using CLSCTX_ALL causes inproc/local objects to fail to load...
COMPtr cp = Ole32.CoCreateInstance(clsid, Ole32.CLSCTX_INPROC_SERVER | Ole32.CLSCTX_LOCAL_SERVER, iid);
// because this is a constructor we can not return cp, but have to "steal" the unknown pointer
// therefore cp.close() are not called
stealUnknown(cp);
}
protected COMPtr(COMPtr comObject) throws COMException {
comObject.queryInterface(this);
}
/**
* for setting the global interface table (GIT) cookie on
* this COMPtr. The GIT cookie can only be set once.
*
* @param peer the GIT cookie.
* @throws COMError if the GIT cookie already set.
* @see #COMPtr(int, int)
*/
void setPeer(int peer) {
if (this.peer != 0) {
throw new COMError("peer already exists");
}
this.peer = peer;
if (peer != 0) {
IdentityManager.incRef(peer);
}
}
/**
* for setting the pointer to the vtable for this COMPtr.
* The vtable pointer can only be set once.
* <br><br>
* Note that this is a very dangerous method to call with
* an unknown reference used on another object, as this will
* lead to two object holding (and releasing) the same reference.
*
* @param unknown the reference to the vtable.
* @throws COMError if the vtable pointer is already set.
* @see #COMPtr(int, int)
*/
void setUnknown(int unknown) {
if (this.unknown != 0) {
throw new COMError("unknown already exists");
}
this.unknown = unknown;
}
/**
* Steal the native unknown pointer from another COMPtr. This should
* only be used inside the marshalling layer when manipulating temporaries.
* This can be seen as a "move" opposed to the "copy" in
* {@link #copyUnknown(COMPtr)}. <b>WARNING:</b> Does NOT increase the reference
* count for this COMPtr.
*
* @param src the COMPtr to steal the unknown pointer from. After this
* the src COMPtr will have an unset unknown pointer (ie. 0).
* @see #copyUnknown(COMPtr)
*/
public void stealUnknown(COMPtr src) {
setUnknown(src.getUnknown());
src.unknown = 0;
if (IdentityManager.TRACE_REFS) {
System.out.println(this + " assigned reference");
}
}
/**
* copy the native unknown pointer from another COMPtr. Increases the reference
* count for this COMPtr by calling AddRef.
*
* @param src the COMPtr to copy the unknown pointer from. The src
* COMPtr is not modified.
* @see #stealUnknown(COMPtr)
*/
protected void copyUnknown(COMPtr src) {
setUnknown(src.getUnknown());
int count = Bootstrap.directCOM(src.getUnknown(), 1); // vtable index 1 is AddRef in IUnknown
if (IdentityManager.TRACE_REFS) {
System.out.println(this + " AddRef = " + count);
}
}
/**
* generic method for calling vtable (virtual function table) based methods on the COM object.
* If the caller is using a {@link NakedByteStream} for building the stack-bytes,
* the {@link #comInvoke(int, String, int, NakedByteStream, Object[])}
* shortcut method is prefered.
*
* @param vtable the index of the requested method in the vtable for this
* COM object. FIXME - point to resource describing how to find
* vtable indexes (the Jawin Type Browser seems to show them as
* vtableoffset?).
* @param instructions the marshalling instructions for marshalling both
* the stack-array onto the native stack (this is the [in]-parameters)
* and marshalling the [return] and [out]-parameters onto the
* returned byte array. Is on the form <code>xx:y:zz</code>,
* where <code>xx</code> is for the [in]-marshalling, <code>y</code>
* is for the [return]-marshalling (this should always be <code>H</code>
* for HRESULT for COM calls), and <code>zz</code> is for any
* [out]-marshalling if present. See the Jawin documentation for
* more about the instruction-strings.
* @param stackSize the size of the call stack on the native side that
* the content of the argStream-array should be marshalled to.
* @param argStreamSize the number of relevant bytes in the argStream-array (often
* a {@link org.jawin.io.NakedByteStream} is used for the argStream-array,
* in which case the array will not be full.
* @param argStream the bytes that should be marshalled to the stack on the native
* side. The length of this array can NOT be smaller than argStreamSize.
* @param objectArgs [in/out] used if any java-object should be passed
* back and forth into the native code.
*
* @return the [return] and [out] parameters from the native call, serialized
* following the marshalling-instructions given the second and third
* part of the instructions string.
*
* @throws COMException if the native method failed.
* @throws ArrayIndexOutOfBoundsException if argStreamSize is bigger than the
* length of the argStream-array.
* @throws IllegalStateException if this COMPtr has been closed or has never
* been initialized correctly (that is both peer and unknown == 0).
*/
public byte[] comInvoke(int vtable, String instructions, int stackSize, int argStreamSize,
byte[] argStream, Object[] objectArgs) throws COMException {
checkState();
return GenericStub.comInvoke(instructions, stackSize, argStreamSize, argStream, objectArgs,
vtable, getIIDToken(), peer, unknown);
}
/**
* shortcut method for calling {@link #comInvoke(int, String, int, int, byte[], Object[])}
* when using a {@link NakedByteStream} for building the argStream-bytes.
*
* @param argStream the NakedByteStream containing the argStream-bytes. Null is
* equal to an empty argStream.
*
* @see #comInvoke(int, String, int, int, byte[], Object[])
*/
public byte[] comInvoke(int vtable, String instructions, int stackSize,
NakedByteStream argStream, Object[] objectArgs) throws COMException {
int argStreamSize = (argStream != null ? argStream.size() : 0);
byte[] argStreamArray = (argStream != null ? argStream.getInternalBuffer() : null);
return comInvoke(vtable, instructions, stackSize, argStreamSize, argStreamArray, objectArgs);
}
public String toString() {
return getClass().getName() + "[" + Integer.toHexString(peer) + "," + Integer.toHexString(unknown) + "]";
}
public synchronized int getPeer() {
return peer;
}
public synchronized int getUnknown() {
return unknown;
}
public abstract int getIIDToken();
/**
* @throws IllegalStateException if this COMPtr has been closed or has never
* been initialized correctly (that is both peer and unknown == 0).
*/
public IUnknown queryInterface(Class newItf) throws COMException {
checkState();
return IdentityManager.queryInterface(newItf, this);
}
/**
* @throws IllegalStateException if this COMPtr has been closed or has never
* been initialized correctly (that is both peer and unknown == 0).
*/
public IUnknown queryGITInterface(Class newItf) throws COMException {
checkState();
return IdentityManager.queryGITInterface(newItf, this);
}
/**
* @throws IllegalStateException if this COMPtr has been closed or has never
* been initialized correctly (that is both peer and unknown == 0).
*/
public IUnknown createDirectRef() throws COMException {
checkState();
return IdentityManager.createDirectRef(this);
}
/**
* @throws IllegalStateException if this COMPtr has been closed or has never
* been initialized correctly (that is both peer and unknown == 0).
*/
public IUnknown createGITRef() throws COMException {
checkState();
return IdentityManager.createGITRef(this);
}
/**
* for retrieving the newItf interface on this object, without instantiating
* a new object.
*
* @param newItf the uninitialized interface-object that should be
* initialized to an interface on this object.
* @return newItf after the GIT cookie or unknown information has been set.
* @throws COMException
*/
private synchronized COMPtr queryInterface(COMPtr newItf) throws COMException {
checkState();
return IdentityManager.queryInterface(newItf, this);
}
/**
* finalize method that calls {@link #close()} as a fallback
* on garbage collection time. Please notice that this fallback
* will not always work, since it is unspecified what thread
* the finalizer will called on.
*/
protected void finalize() throws Throwable {
if (IdentityManager.TRACE_REFS && (unknown != 0)) {
System.out.println("****Failed to release reference to " + this);
}
close();
super.finalize();
}
/**
* Should be called when finished using this COMPtr. Decreases
* the reference-counts to either the GIT-cookie or
* the vtable interface pointer.
* <br><br>
* <b>Important:</b> This should be called on the same thread as
* this object is created on (this is not guarenteed for the
* {@link #finalize()} method, which can lead to leaks).
*/
public synchronized void close() throws COMException {
releaseUnknown();
releasePeer();
}
/**
* call only from a synchronized method
*/
private void releaseUnknown() {
if (unknown != 0) {
int count = Bootstrap.directCOM(unknown, 2); // vtable index 2 is Release in IUnknown
if (IdentityManager.TRACE_REFS) {
System.out.println(this +" Release = " + count);
}
unknown = 0;
}
}
/**
* call only from a synchronized method
*/
private void releasePeer() throws COMException {
if (peer != 0) {
IdentityManager.decRef(peer);
peer = 0;
}
}
/**
* @throws IllegalStateException if this COMPtr has been closed or has never
* been initialized correctly (that is both peer and unknown == 0).
*/
private void checkState() {
if ((unknown == 0) && (peer == 0)) {
throw new IllegalStateException(toString() + " has been closed or has never been initialized");
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -