⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 comptr.java

📁 java 调用windows的api
💻 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, &lt;CLASS&gt;.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 + -