From 4a0f25c8f11962620f22bc613607a4ab06d1c082 Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Thu, 29 Jan 2009 22:14:13 +0000 Subject: [PATCH] import: tcom-3.8 import --- CHANGES | 217 +++ LICENSE | 26 + README | 23 + doc/Makefile | 9 + doc/article2html.xsl | 143 ++ doc/bankingClassDiagram.png | Bin 0 -> 4678 bytes doc/docbookx.dtd | 0 doc/refentry2html.xsl | 173 ++ doc/server.html | 291 ++++ doc/server.xml | 281 +++ doc/tcom.n.html | 597 +++++++ doc/tcom.n.xml | 593 +++++++ doc/xslt.tcl | 47 + lib/Banking/Banking.tlb | Bin 0 -> 2396 bytes lib/Banking/pkgIndex.tcl | 2 + lib/Banking/server.itcl | 34 + lib/Banking/server.tcl | 47 + lib/TclScript/TclScript.dll | Bin 0 -> 32833 bytes lib/TclScript/TclScript.itcl | 422 +++++ lib/TclScript/TclScript.tlb | Bin 0 -> 20252 bytes lib/TclScript/pkgIndex.tcl | 3 + lib/TclScript/register.tcl | 34 + lib/tcom/pkgIndex.tcl | 3 + lib/tcom/tcom.dll | Bin 0 -> 262194 bytes lib/tcom/tcom.tcl | 152 ++ lib/tcom/tcominproc.dll | Bin 0 -> 32834 bytes lib/tcom/tcomlocal.exe | Bin 0 -> 32833 bytes samples/Banking/Banking.idl | 62 + samples/Banking/client.tcl | 9 + samples/chart.tcl | 43 + samples/events.tcl | 20 + samples/excel.tcl | 50 + samples/sendkeys.tcl | 13 + src/ActiveScriptError.cpp | 66 + src/ActiveScriptError.h | 49 + src/Arguments.cpp | 308 ++++ src/Arguments.h | 124 ++ src/ComModule.cpp | 108 ++ src/ComModule.h | 69 + src/ComObject.cpp | 844 +++++++++ src/ComObject.h | 145 ++ src/ComObjectFactory.cpp | 179 ++ src/ComObjectFactory.h | 96 ++ src/Extension.cpp | 99 ++ src/Extension.h | 103 ++ src/HandleSupport.cpp | 276 +++ src/HandleSupport.h | 182 ++ src/HashTable.h | 176 ++ src/InterfaceAdapter.cpp | 145 ++ src/InterfaceAdapter.h | 101 ++ src/InterfaceAdapterVtbl.cpp | 3131 ++++++++++++++++++++++++++++++++++ src/Makefile | 21 + src/Reference.cpp | 588 +++++++ src/Reference.h | 179 ++ src/RegistryKey.cpp | 81 + src/RegistryKey.h | 34 + src/Singleton.h | 59 + src/SupportErrorInfo.cpp | 27 + src/SupportErrorInfo.h | 30 + src/TclInterp.cpp | 124 ++ src/TclInterp.h | 50 + src/TclModule.cpp | 42 + src/TclModule.h | 29 + src/TclObject.cpp | 610 +++++++ src/TclObject.h | 123 ++ src/TclScript.cpp | 230 +++ src/TclScript.dsp | 125 ++ src/TclScript.idl | 27 + src/TclScriptVersion.rc | 35 + src/ThreadLocalStorage.h | 64 + src/TypeInfo.cpp | 645 +++++++ src/TypeInfo.h | 455 +++++ src/TypeLib.cpp | 366 ++++ src/TypeLib.h | 140 ++ src/Uuid.cpp | 14 + src/Uuid.h | 34 + src/bindCmd.cpp | 238 +++ src/buildNumber.h | 1 + src/comsupp.cpp | 60 + src/configureCmd.cpp | 95 ++ src/dllmain.cpp | 81 + src/dllserver.def | 5 + src/dllserver.dsp | 139 ++ src/dllserverVersion.rc | 35 + src/exemain.cpp | 119 ++ src/exeserver.dsp | 135 ++ src/exeserverVersion.rc | 35 + src/foreachCmd.cpp | 185 ++ src/importCmd.cpp | 534 ++++++ src/infoCmd.cpp | 269 +++ src/main.cpp | 66 + src/mutex.h | 73 + src/naCmd.cpp | 93 + src/nullCmd.cpp | 58 + src/objectCmd.cpp | 288 ++++ src/refCmd.cpp | 772 +++++++++ src/resource.h | 15 + src/shortPathNameCmd.cpp | 25 + src/tclRunTime.h | 16 + src/tcom.dsp | 353 ++++ src/tcom.dsw | 74 + src/tcomApi.h | 13 + src/tcomVersion.rc | 35 + src/typelibCmd.cpp | 256 +++ src/version.h | 14 + tests/all.tcl | 20 + tests/foreach.test | 42 + tests/namedarg.test | 49 + tests/ref.test | 52 + 109 files changed, 17872 insertions(+) create mode 100644 CHANGES create mode 100644 LICENSE create mode 100644 README create mode 100644 doc/Makefile create mode 100644 doc/article2html.xsl create mode 100644 doc/bankingClassDiagram.png create mode 100644 doc/docbookx.dtd create mode 100644 doc/refentry2html.xsl create mode 100644 doc/server.html create mode 100644 doc/server.xml create mode 100644 doc/tcom.n.html create mode 100644 doc/tcom.n.xml create mode 100644 doc/xslt.tcl create mode 100644 lib/Banking/Banking.tlb create mode 100644 lib/Banking/pkgIndex.tcl create mode 100644 lib/Banking/server.itcl create mode 100644 lib/Banking/server.tcl create mode 100644 lib/TclScript/TclScript.dll create mode 100644 lib/TclScript/TclScript.itcl create mode 100644 lib/TclScript/TclScript.tlb create mode 100644 lib/TclScript/pkgIndex.tcl create mode 100644 lib/TclScript/register.tcl create mode 100644 lib/tcom/pkgIndex.tcl create mode 100644 lib/tcom/tcom.dll create mode 100644 lib/tcom/tcom.tcl create mode 100644 lib/tcom/tcominproc.dll create mode 100644 lib/tcom/tcomlocal.exe create mode 100644 samples/Banking/Banking.idl create mode 100644 samples/Banking/client.tcl create mode 100644 samples/chart.tcl create mode 100644 samples/events.tcl create mode 100644 samples/excel.tcl create mode 100644 samples/sendkeys.tcl create mode 100644 src/ActiveScriptError.cpp create mode 100644 src/ActiveScriptError.h create mode 100644 src/Arguments.cpp create mode 100644 src/Arguments.h create mode 100644 src/ComModule.cpp create mode 100644 src/ComModule.h create mode 100644 src/ComObject.cpp create mode 100644 src/ComObject.h create mode 100644 src/ComObjectFactory.cpp create mode 100644 src/ComObjectFactory.h create mode 100644 src/Extension.cpp create mode 100644 src/Extension.h create mode 100644 src/HandleSupport.cpp create mode 100644 src/HandleSupport.h create mode 100644 src/HashTable.h create mode 100644 src/InterfaceAdapter.cpp create mode 100644 src/InterfaceAdapter.h create mode 100644 src/InterfaceAdapterVtbl.cpp create mode 100644 src/Makefile create mode 100644 src/Reference.cpp create mode 100644 src/Reference.h create mode 100644 src/RegistryKey.cpp create mode 100644 src/RegistryKey.h create mode 100644 src/Singleton.h create mode 100644 src/SupportErrorInfo.cpp create mode 100644 src/SupportErrorInfo.h create mode 100644 src/TclInterp.cpp create mode 100644 src/TclInterp.h create mode 100644 src/TclModule.cpp create mode 100644 src/TclModule.h create mode 100644 src/TclObject.cpp create mode 100644 src/TclObject.h create mode 100644 src/TclScript.cpp create mode 100644 src/TclScript.dsp create mode 100644 src/TclScript.idl create mode 100644 src/TclScriptVersion.rc create mode 100644 src/ThreadLocalStorage.h create mode 100644 src/TypeInfo.cpp create mode 100644 src/TypeInfo.h create mode 100644 src/TypeLib.cpp create mode 100644 src/TypeLib.h create mode 100644 src/Uuid.cpp create mode 100644 src/Uuid.h create mode 100644 src/bindCmd.cpp create mode 100644 src/buildNumber.h create mode 100644 src/comsupp.cpp create mode 100644 src/configureCmd.cpp create mode 100644 src/dllmain.cpp create mode 100644 src/dllserver.def create mode 100644 src/dllserver.dsp create mode 100644 src/dllserverVersion.rc create mode 100644 src/exemain.cpp create mode 100644 src/exeserver.dsp create mode 100644 src/exeserverVersion.rc create mode 100644 src/foreachCmd.cpp create mode 100644 src/importCmd.cpp create mode 100644 src/infoCmd.cpp create mode 100644 src/main.cpp create mode 100644 src/mutex.h create mode 100644 src/naCmd.cpp create mode 100644 src/nullCmd.cpp create mode 100644 src/objectCmd.cpp create mode 100644 src/refCmd.cpp create mode 100644 src/resource.h create mode 100644 src/shortPathNameCmd.cpp create mode 100644 src/tclRunTime.h create mode 100644 src/tcom.dsp create mode 100644 src/tcom.dsw create mode 100644 src/tcomApi.h create mode 100644 src/tcomVersion.rc create mode 100644 src/typelibCmd.cpp create mode 100644 src/version.h create mode 100644 tests/all.tcl create mode 100644 tests/foreach.test create mode 100644 tests/namedarg.test create mode 100644 tests/ref.test diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..1ce6379 --- /dev/null +++ b/CHANGES @@ -0,0 +1,217 @@ +Version 3.8 +- Fixed defect which prevented DISPATCH_PROPERTYPUTREF properties from being + set. +- Fixed defect which incorrectly decremented reference count of interface + pointers passed as method arguments. +- Fixed defect where only one argument was passed to methods marked with the + [vararg] attribute. +- Fixed defect with handling of [out] SAFEARRAY parameters. +- Implemented work around for access violation when ::tcom::import command + was used with Excel. +- Implemented work around for incorrect IDispatch implementation in Microsoft + Word. + +Version 3.7 +- Fixed access violation in ::tcom::foreach command. + +Version 3.6 +- Prevent race conditions when local server registers multiple class objects. +- Added work around for bogus type information returned from IDispatch + implemented by AutoDispatch in Microsoft .NET Framework. + +Version 3.5 +- Fixed defect where the code tried to invoke operations on pure dispatch + interfaces by calling through the virtual function table. + +Version 3.4 +- Added -inproc, -local and -remote options to "::tcom::ref createobject" + command. +- Fixed access violation when IDispatch objects return a null error + description. +- The "::tcom::object create" and "::tcom::object registerfactory" commands + accept the -registeractive option which registers the created COM object in + the running object table. +- Added -inproc and -local options to "::tcom::server register" command. + +Version 3.3 +- The "::tcom::ref getobject" command now gets a reference to a COM object + specified by a moniker name. Added the "::tcom::ref getactiveobject" + command which gets a reference to an existing COM object specified by a + programmatic ID. +- Added -method, -get and -set options for invoking operations on IDispatch + interfaces. +- Fixed defect where passing an interface pointer as an argument to a COM + method caused Release to be called too many times on the interface pointer. + +Version 3.2 +- Fixed defect where ::tcom::foreach command called Release one too many times + on the interface pointer to the collection object. +- The "::tcom::object create" command can now accept a list of imported + interface names to implement. + +Version 3.1 +- Fixed access violation when formatting COM error message. +- Add clean up code to restore original Tcl cmdName type, so wish will not hang + on exit. + +Version 3.0 +- Handles now automatically released. Removed "::tcom::release" command. +- Added "::tcom::foreach" command which iterates through the elements of a + COM collection. + +Version 2.8 +- Provide work around for dual interface objects that don't implement IDispatch + correctly (such as Visual Studio). Now references created by class commands + generated by the ::tcom::import command will try to invoke operations through + the virtual function table before trying through IDispatch. +- Fixed defect where only the last connection point is unadvised and released + when more than one event sink is bound to an object reference. +- Added "::tcom::ref equal" command which test handles for COM identity. +- Added "::tcom::unbind" command which disconnects event sinks. +- VARIANT dates (VT_DATE) are now converted to Tcl double values. + +Version 2.7 +- Added support for one-dimensional SAFEARRAYs of primitive data types as + method arguments. + +Version 2.6 +- Fixed type mismatch error when invoking methods with an [out] IDispatch + parameter. +- Fixed bug where tcom server methods invoked through the virtual function + table kept a reference to input COM interface pointer arguments. +- Fixed bug where Tcl error result formatting did not provide text + descriptions of COM error codes on Windows 95. + +Version 2.5 +- Fixed bug where [out, retval] parameters were not treated as method return + values. +- Fixed bug where property put functions could not be implemented because + descriptions for those functions were discarded. + +Version 2.4 +- COM objects now run as in-process and local servers using the Tcl package + mechanism to load object implementations. Added the ::tcom::server command + to register and unregister servers. +- The ::tcom::object create command now optionally specifies a Tcl command to + execute when the object is destroyed. +- Removed the -register option from the ::tcom::object create command. Now use + the ::tcom::object registerfactory command to register a class factory. +- Deprecated -property option, which is now silently ignored. MIDL should + never allow a property and method to have the same name. + +Version 2.3 +- Fixed illegal memory access bug when importing type libraries. +- Fixed bug where strings passed in BSTR arguments were not converted from + UTF-8. + +Version 2.2 +- Fixed bug where duplication method descriptions were stored when traversing + inherited interfaces. +- Catch invalid callee error when a type library says that an object implements + a dual interface when it actually doesn't. + +Version 2.1 +- Fixed bug where the ::tcom::import command did not read TKIND_DISPATCH type + information. +- Fixed bug where parameters that were pointer types were treated as out + parameters when they should have been in parameters. + +Version 2.0 +- The ::tcom::bind command now binds a Tcl command to events generated by an + object. Use the ::tcom::ref command now to create a reference to an object. +- The default concurrency model on Windows NT is now apartment threaded. + Use the ::tcom::configure command to set the concurrency model. +- The ::tcom::import command now returns the library name stored in the type + library file. + +Version 1.11 +- Fixed type mismatch error when calling an IDispatch method that returns + a VARIANT of type VT_NULL. +- Convert boolean Tcl internal representation to VARIANT boolean type. +- Can now access properties of IDispatch implementations that describe their + properties using the variable descriptions instead of the function + descriptions in their type information. +- The object created by the ::tcom::object command now processes named + arguments passed to its Invoke method by converting them into a Tcl list of + argument names and values. + +Version 1.10 +- Fixed empty Tcl error message returned upon attempt to read a non-existent + variable when passing an [in,out] argument. +- Fixed bug where the reference count of COM objects created by the + ::tcom::object command can never be decremented to 0. +- Fixed passing of missing optional arguments. +- Added ismissing subcommand to ::tcom::na command. This checks if an object + is a missing argument token. + +Version 1.9 +- The life cycle of handle objects can be managed by multiple Tcl interpreters. +- Now convert Tcl int and long object types to VARIANT integer type when + passing arguments. This enables the use of integer values to index into + collections. +- Replaced tlib2tcl.tcl script with ::tcom::import command. + +Version 1.8 + +- Added -getobject option to ::tcom::bind command to get a reference to an + existing object. +- Removed static initialization of C++ objects to allow the extension to use + Tcl stubs. + +Version 1.7 + +- Now you don't have to specify all the arguments when calling an object method. + A VARIANT value denoting a missing value will be passed in place of the + missing arguments. +- The ::tcom::bind command and class commands generated by tlib2tcl now accept + the -withevents option to allow Tcl scripting of event sinks. +- Fixed the IDispatch server implementation to follow the correct memory + management rules for events posted on the Tcl event queue. +- If the type information for the interface is available, the type + information will be attached to interface pointers returned from methods. + +Version 1.6 + +- Added ::tcom::object command that allows IDispatch interfaces to be + implemented in Tcl. +- The ::tcom::bind command now accepts the -clsid option for specifying a + class by CLSID instead of programmatic ID. +- The tlib2tcl utility now puts the UUID of interfaces and classes into an + array named __uuidof indexed by their name. +- Fixed COM interface pointer leak that occurred when a method returned an + interface pointer. +- Made the code thread safe. + +Version 1.5 + +- Added ::tcom::dispatch and ::tcom::na commands. +- Added sample Tcl script which shows how to create a spreadsheet by taking + control of Excel. + +Version 1.4 + +- Replaced "$interfaceInfo method" command with "$interfaceInfo methods" which + returns a list of method descriptions. +- The tlib2tcl utility now generates Tcl code that requires the type library + file at run time. +- Support Tcl 8.1 features: + - A Tcl byte array argument passed to an interface method is converted to a + one dimensional SAFEARRAY of bytes (VT_UI1). + +Version 1.3 + +- Enhanced tlib2tcl to also generate Tcl arrays for enumerations defined in + the type library. +- Class commands initialized object references with interface descriptions but + the references discarded them on a createInstance. +- Now uninitialize COM when the Tcl interpreter is deleted. + +Version 1.2 + +- Now allow property access using object reference command. +- Fixed array of VARIANT to Tcl list conversion. + +Version 1.1 + +- The tlib2tcl utility was outputing "VOID" types as "unknown". +- Added tcom95.dll which does not use Windows NT specific COM features. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..59f5603 --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +This software is copyrighted by Chin Huang and other parties. The +following terms apply to all files associated with the software unless +explicitly disclaimed in individual files. + +The authors hereby grant permission to use, copy, modify, distribute, +and license this software and its documentation for any purpose, provided +that existing copyright notices are retained in all copies and that this +notice is included verbatim in any distributions. No written agreement, +license, or royalty fee is required for any of the authorized uses. +Modifications to this software may be copyrighted by their authors +and need not follow the licensing terms described here, provided that +the new terms are clearly indicated on the first page of each file where +they apply. + +IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +MODIFICATIONS. diff --git a/README b/README new file mode 100644 index 0000000..e013838 --- /dev/null +++ b/README @@ -0,0 +1,23 @@ +Tcom is a Windows-specific Tcl extension that provides commands to access and +implement COM objects. This extension enables client-side and server-side +scripting of COM objects through IDispatch and IUnknown derived interfaces. + +INSTALLATION + +This distribution includes compiled libraries that can be loaded by the binary +release of Tcl/Tk 8.2 or later for Windows. Copy the contents of the lib +directory to the Tcl library directory. For example, if the Tcl library +directory is C:\Tcl\lib, enter this command at the command prompt: + + xcopy lib C:\Tcl\lib /s + + +TCL ACTIVE SCRIPT ENGINE + +This distribution includes an Active Script engine that's currently in a +pre-alpha stage of development. It implements just enough of the IActiveScript +and IActiveScriptParse interfaces to enable Internet Explorer and Windows +Script Host to run simple scripts. It works with the ActiveTcl binary +distribution from ActiveState. To register the script engine (assuming the +Tcl library directory is C:\Tcl\lib), change the current working directory to +C:\Tcl\lib\TclScript and run the register.tcl script. diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..40f59cf --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,9 @@ +# $Id: Makefile,v 1.6 2002/04/17 22:07:57 cthuang Exp $ + +all: tcom.n.html server.html + +tcom.n.html: tcom.n.xml xslt.tcl refentry2html.xsl + tclsh xslt.tcl tcom.n.xml refentry2html.xsl $@ + +server.html: server.xml xslt.tcl article2html.xsl + tclsh xslt.tcl server.xml article2html.xsl $@ diff --git a/doc/article2html.xsl b/doc/article2html.xsl new file mode 100644 index 0000000..06c72ad --- /dev/null +++ b/doc/article2html.xsl @@ -0,0 +1,143 @@ + + + + + + + + + <xsl:value-of select="artheader/title"/> + + + +

+ + + +
+ + + + + + + + + + + + ? + + + + ... + + + + ? + + + + + + + + + + + + + +

+
+ + + + + + +
+
+ + +
+ +
+
+ + + + + + +
+
+ + +
+
+ + +

+
+ + + + + + + + + + +
+    
+    
+
+ + +
+    
+    
+
+ + + + + + +
+
+ + + + + + + + + + + + < + + > + + </ + + > + + + +
diff --git a/doc/bankingClassDiagram.png b/doc/bankingClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..831f3a3bb1633c41d62f02a8b3b8ca41b3f2faab GIT binary patch literal 4678 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 + + + + + + + + <xsl:value-of select="refnamediv/refname"/> + + + +

Name

+

--

+ + + +
+ + + + + + + + +

Synopsis

+ +
+ + + + + + + + + + ? + + + + ... + + + + ? + + + + + + + + + + + + + +

+
+ + + + + + +

+
+ + + + + + +
+
+ + +
+ +
+
+ + + + + + +
+
+ + +
+
+ + +

+
+ + + + + + + + + + +
+ +
+
+ + +
+
+ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+    
+    
+
+ + + + < + + > + + </ + + > + + + +
diff --git a/doc/server.html b/doc/server.html new file mode 100644 index 0000000..d4f1386 --- /dev/null +++ b/doc/server.html @@ -0,0 +1,291 @@ + + + +COM Object Implementation in Tcl + + + +

COM Object Implementation in Tcl

+ + +

Introduction

+

This article shows by example how to implement COM objects in + Tcl with the tcom extension. It shows how an object + can be implemented by an [incr Tcl] class or in just plain Tcl. +

+
+ + + +
+

The class diagram shows the structure of server objects which implement + two COM interfaces. The IAccount interface defines a Balance property, and + Deposit and Withdraw methods which modify the balance. The Account class + implements the IAccount interface by delegating its operations to the + AccountImpl class, which is written in [incr Tcl] and actually implements + the operations. The IBank interface defines a method to create an account. + Following the same pattern, the Bank class implements the IBank interface by + delegating to the BankImpl class, which provides the actual implementation. +

+ + +

Write MIDL Specification

+

The file Banking.idl contains the MIDL + specification for the COM interfaces and classes. The interfaces can be + declared dual because tcom can + implement objects whose operations are invoked through the IDispatch + interface or the virtual function table.

+
+
+import "oaidl.idl";
+import "ocidl.idl";
+
+        [
+                object,
+                uuid(0A0059C4-E0B0-11D2-942A-00C04F7040AB),
+                dual,
+                helpstring("IAccount Interface"),
+                pointer_default(unique)
+        ]
+        interface IAccount: IDispatch
+        {
+                [id(1), propget, helpstring("property Balance")]
+                HRESULT Balance([out, retval] long *pValue);
+
+                [id(2), helpstring("method Deposit")]
+                HRESULT Deposit([in] long amount);
+
+                [id(3), helpstring("method Withdraw")]
+                HRESULT Withdraw([in] long amount);
+        };
+
+        [
+                object,
+                uuid(0A0059C4-E0B0-11D2-942A-00C04F7040AC),
+                dual,
+                helpstring("IBank Interface"),
+                pointer_default(unique)
+        ]
+        interface IBank: IDispatch
+        {
+                [id(1), helpstring("method CreateAccount")]
+                HRESULT CreateAccount([out, retval] IAccount **pAccount);
+        };
+
+[
+        uuid(0A0059B8-E0B0-11D2-942A-00C04F7040AB),
+        version(1.0),
+        helpstring("Banking 1.0 Type Library")
+]
+library Banking
+{
+        importlib("stdole32.tlb");
+
+        [
+                uuid(0A0059C5-E0B0-11D2-942A-00C04F7040AB),
+                helpstring("Account Class")
+        ]
+        coclass Account
+        {
+                [default] interface IAccount;
+        };
+
+        [
+                uuid(0A0059C5-E0B0-11D2-942A-00C04F7040AC),
+                helpstring("Bank Class")
+        ]
+        coclass Bank
+        {
+                [default] interface IBank;
+        };
+};
+
+ + +

Create Type Library

+

Run this command to generate a type library file + Banking.tlb from the MIDL specification.

+
+
+midl Banking.idl
+
+ + +

Create Tcl Package

+

The tcom server implementation depends on the Tcl + package mechanism to provide the code that implements specific COM interfaces. + In this example, we'll create a package named Banking, which provides code + that implements the IBank and IAccount interfaces.

+ +

Create a directory for the package by making a subdirectory named + Banking under one of the directories in the + auto_path variable. Create a + pkgIndex.tcl file in the package directory.

+
+
+package ifneeded Banking 1.0 [list source [file join $dir server.itcl]]
+
+ +

Copy the Banking.tlb type library file into the + package directory.

+ +

Create the following server.itcl file in the package + directory. This file defines [incr Tcl] classes that implement the + IBank and IAccount interfaces.

+ +
+
+package provide Banking 1.0
+
+package require Itcl
+namespace import ::itcl::*
+
+package require tcom
+::tcom::import [file join [file dirname [info script]] Banking.tlb]
+
+class AccountImpl {
+    private variable balance 0
+
+    public method _get_Balance {} {
+        return $balance
+    }
+
+    public method Deposit {amount} {
+        set balance [expr $balance + $amount]
+    }
+
+    public method Withdraw {amount} {
+        set balance [expr $balance - $amount]
+    }
+}
+
+class BankImpl {
+    public method CreateAccount {} {
+        set accountImpl [AccountImpl #auto]
+        return [::tcom::object create ::Banking::Account \
+            [code $accountImpl] {delete object}]                                ;# 1
+    }
+}
+
+::tcom::object registerfactory ::Banking::Bank {BankImpl #auto} {delete object} ;# 2
+
+ +

On line 1, the ::tcom::object create command creates + a COM object that implements the IAccount interface by delegating its + operations to an [incr Tcl] object specified by an [incr Tcl] object handle. + Interface methods are mapped to a method with the same name. Interface + properties are mapped to methods named by prepending _get_ + and _set_ to the property name. When the last reference + to the COM object is released, tcom invokes the + delete object command with the [incr Tcl] object handle as + an additional argument to clean up the [incr Tcl] object.

+ +

Line 2 creates a factory for creating instances of the Bank class and + registers the factory with COM. To create a COM object, the factory invokes + a command which returns a handle to an [incr Tcl] object that implements the + operations. In this example, the factory invokes the BankImpl + #auto command which creates a BankImpl [incr Tcl] object and + returns a handle to that object. To clean up when the COM object is + destroyed, tcom invokes the delete + object command with the [incr Tcl] object handle as an additional + argument.

+ + +

Register Server

+

Run these Tcl commands to create entries in the Windows registry + required by COM and the tcom server implementation. +

+
+
+package require tcom
+::tcom::server register Banking.tlb
+
+ + +

Implement Client

+

The client.tcl script implements a simple client. + It gets a reference to an object that implements the bank interface, creates + an account, and performs some operations on the account.

+
+
+package require tcom
+
+set bank [::tcom::ref createobject "Banking.Bank"]
+set account [$bank CreateAccount]
+puts [$account Balance]
+$account Deposit 20
+puts [$account Balance]
+$account Withdraw 10
+puts [$account Balance]
+
+ + +

Implement Objects In Plain Tcl

+

You can implement objects in plain Tcl. The servant command passed to + the ::tcom::object create command can be the name of any + object-style command. Similarly, the factory command passed to the + ::tcom::object registerfactory command can return the + name of any object-style command. The following Tcl script defines the + procedures accountImpl and bankImpl, + which have parameters in the style of a method name followed by any + arguments.

+
+
+package provide Banking 1.0
+
+package require tcom
+::tcom::import [file join [file dirname [info script]] Banking.tlb]
+
+proc accountImpl {method args} {
+    global balance
+
+    switch -- $method {
+        _get_Balance {
+            return $balance
+        }
+
+        Deposit {
+            set amount [lindex $args 0]
+            set balance [expr $balance + $amount]
+        }
+
+        Withdraw {
+            set amount [lindex $args 0]
+            set balance [expr $balance - $amount]
+        }
+        
+        default {
+            error "unknown method $method $args"
+        }
+    }
+}
+
+proc bankImpl {method args} {
+    global balance
+
+    switch -- $method {
+        CreateAccount {
+            set balance 0
+            return [::tcom::object create ::Banking::Account accountImpl]
+        }
+        
+        default {
+            error "unknown method $method $args"
+        }
+    }
+}
+
+::tcom::object registerfactory ::Banking::Bank {list bankImpl}
+
+ + + diff --git a/doc/server.xml b/doc/server.xml new file mode 100644 index 0000000..fec1814 --- /dev/null +++ b/doc/server.xml @@ -0,0 +1,281 @@ + + + +
+ + $Date: 2002/06/29 15:34:52 $ + $Revision: 1.23 $ + COM Object Implementation in Tcl + + + Introduction + This article shows by example how to implement COM objects in + Tcl with the tcom extension. It shows how an object + can be implemented by an [incr Tcl] class or in just plain Tcl. + + + + + + + The class diagram shows the structure of server objects which implement + two COM interfaces. The IAccount interface defines a Balance property, and + Deposit and Withdraw methods which modify the balance. The Account class + implements the IAccount interface by delegating its operations to the + AccountImpl class, which is written in [incr Tcl] and actually implements + the operations. The IBank interface defines a method to create an account. + Following the same pattern, the Bank class implements the IBank interface by + delegating to the BankImpl class, which provides the actual implementation. + + + + Write MIDL Specification + The file Banking.idl contains the MIDL + specification for the COM interfaces and classes. The interfaces can be + declared dual because tcom can + implement objects whose operations are invoked through the IDispatch + interface or the virtual function table. + + +import "oaidl.idl"; +import "ocidl.idl"; + + [ + object, + uuid(0A0059C4-E0B0-11D2-942A-00C04F7040AB), + dual, + helpstring("IAccount Interface"), + pointer_default(unique) + ] + interface IAccount: IDispatch + { + [id(1), propget, helpstring("property Balance")] + HRESULT Balance([out, retval] long *pValue); + + [id(2), helpstring("method Deposit")] + HRESULT Deposit([in] long amount); + + [id(3), helpstring("method Withdraw")] + HRESULT Withdraw([in] long amount); + }; + + [ + object, + uuid(0A0059C4-E0B0-11D2-942A-00C04F7040AC), + dual, + helpstring("IBank Interface"), + pointer_default(unique) + ] + interface IBank: IDispatch + { + [id(1), helpstring("method CreateAccount")] + HRESULT CreateAccount([out, retval] IAccount **pAccount); + }; + +[ + uuid(0A0059B8-E0B0-11D2-942A-00C04F7040AB), + version(1.0), + helpstring("Banking 1.0 Type Library") +] +library Banking +{ + importlib("stdole32.tlb"); + + [ + uuid(0A0059C5-E0B0-11D2-942A-00C04F7040AB), + helpstring("Account Class") + ] + coclass Account + { + [default] interface IAccount; + }; + + [ + uuid(0A0059C5-E0B0-11D2-942A-00C04F7040AC), + helpstring("Bank Class") + ] + coclass Bank + { + [default] interface IBank; + }; +}; + + + + Create Type Library + Run this command to generate a type library file + Banking.tlb from the MIDL specification. + + +midl Banking.idl + + + + Create Tcl Package + The tcom server implementation depends on the Tcl + package mechanism to provide the code that implements specific COM interfaces. + In this example, we'll create a package named Banking, which provides code + that implements the IBank and IAccount interfaces. + + Create a directory for the package by making a subdirectory named + Banking under one of the directories in the + auto_path variable. Create a + pkgIndex.tcl file in the package directory. + + +package ifneeded Banking 1.0 [list source [file join $dir server.itcl]] + + + Copy the Banking.tlb type library file into the + package directory. + + Create the following server.itcl file in the package + directory. This file defines [incr Tcl] classes that implement the + IBank and IAccount interfaces. + + + +package provide Banking 1.0 + +package require Itcl +namespace import ::itcl::* + +package require tcom +::tcom::import [file join [file dirname [info script]] Banking.tlb] + +class AccountImpl { + private variable balance 0 + + public method _get_Balance {} { + return $balance + } + + public method Deposit {amount} { + set balance [expr $balance + $amount] + } + + public method Withdraw {amount} { + set balance [expr $balance - $amount] + } +} + +class BankImpl { + public method CreateAccount {} { + set accountImpl [AccountImpl #auto] + return [::tcom::object create ::Banking::Account \ + [code $accountImpl] {delete object}] ;# 1 + } +} + +::tcom::object registerfactory ::Banking::Bank {BankImpl #auto} {delete object} ;# 2 + + + On line 1, the ::tcom::object create command creates + a COM object that implements the IAccount interface by delegating its + operations to an [incr Tcl] object specified by an [incr Tcl] object handle. + Interface methods are mapped to a method with the same name. Interface + properties are mapped to methods named by prepending _get_ + and _set_ to the property name. When the last reference + to the COM object is released, tcom invokes the + delete object command with the [incr Tcl] object handle as + an additional argument to clean up the [incr Tcl] object. + + Line 2 creates a factory for creating instances of the Bank class and + registers the factory with COM. To create a COM object, the factory invokes + a command which returns a handle to an [incr Tcl] object that implements the + operations. In this example, the factory invokes the BankImpl + #auto command which creates a BankImpl [incr Tcl] object and + returns a handle to that object. To clean up when the COM object is + destroyed, tcom invokes the delete + object command with the [incr Tcl] object handle as an additional + argument. + + + Register Server + Run these Tcl commands to create entries in the Windows registry + required by COM and the tcom server implementation. + + + +package require tcom +::tcom::server register Banking.tlb + + + + Implement Client + The client.tcl script implements a simple client. + It gets a reference to an object that implements the bank interface, creates + an account, and performs some operations on the account. + + +package require tcom + +set bank [::tcom::ref createobject "Banking.Bank"] +set account [$bank CreateAccount] +puts [$account Balance] +$account Deposit 20 +puts [$account Balance] +$account Withdraw 10 +puts [$account Balance] + + + + Implement Objects In Plain Tcl + You can implement objects in plain Tcl. The servant command passed to + the ::tcom::object create command can be the name of any + object-style command. Similarly, the factory command passed to the + ::tcom::object registerfactory command can return the + name of any object-style command. The following Tcl script defines the + procedures accountImpl and bankImpl, + which have parameters in the style of a method name followed by any + arguments. + + +package provide Banking 1.0 + +package require tcom +::tcom::import [file join [file dirname [info script]] Banking.tlb] + +proc accountImpl {method args} { + global balance + + switch -- $method { + _get_Balance { + return $balance + } + + Deposit { + set amount [lindex $args 0] + set balance [expr $balance + $amount] + } + + Withdraw { + set amount [lindex $args 0] + set balance [expr $balance - $amount] + } + + default { + error "unknown method $method $args" + } + } +} + +proc bankImpl {method args} { + global balance + + switch -- $method { + CreateAccount { + set balance 0 + return [::tcom::object create ::Banking::Account accountImpl] + } + + default { + error "unknown method $method $args" + } + } +} + +::tcom::object registerfactory ::Banking::Bank {list bankImpl} + + +
diff --git a/doc/tcom.n.html b/doc/tcom.n.html new file mode 100644 index 0000000..6913361 --- /dev/null +++ b/doc/tcom.n.html @@ -0,0 +1,597 @@ + + + +tcom + + + +

Name

+

tcom -- Access COM objects from Tcl

+ + + +

Synopsis

+ + package require tcom + ?3.8? +
+ ::tcom::ref + createobject + ?-inproc? + ?-local? + ?-remote? + ?-clsid? + progID + ?hostName? +
+ ::tcom::ref + getactiveobject + ?-clsid? + progID +
+ ::tcom::ref + getobject + pathName +
+ ::tcom::ref + equal + handle1 + handle2 +
+ handle + ?-method? + method + ?argument ...? +
+ handle + -namedarg + method + ?argumentName argumentValue ...? +
+ handle + ?-get? + ?-set? + property + ?index ...? + ?value? +
+ ::tcom::foreach + varname + collectionHandle + body +
+ ::tcom::foreach + varlist + collectionHandle + body +
+ ::tcom::bind + handle + command + ?eventIID? +
+ ::tcom::unbind + handle +
+ ::tcom::na +
+ ::tcom::info interface + handle +
+ ::tcom::configure + name + ?value? +
+ ::tcom::import + typeLibrary + ?namespace? +
+ + + +

Description

+

The tcom package provides commands to access COM + objects through IDispatch and IUnknown derived interfaces.

+ + +

Commands

+
+ +
+ + ::tcom::ref + createobject + ?-inproc? + ?-local? + ?-remote? + ?-clsid? + progID + ?hostName? +
+ ::tcom::ref + getactiveobject + ?-clsid? + progID + +
+
+

These commands return a handle representing a reference to a COM + object through an interface pointer. The handle can be used as a Tcl + command to invoke operations on the object. In practice, you should store + the handle in a Tcl variable or pass it as an argument to another command. +

+

References to COM objects are automatically released. If you store + the handle in a local variable, the reference is released when execution + leaves the variable's scope. If you store the handle in a global + variable, you can release the reference by unsetting the variable, setting + the variable to another value, or exiting the Tcl interpreter.

+

The createobject subcommand creates an instance + of the object. The -inproc option requests the object be + created in the same process. The -local option requests + the object be created in another process on the local machine. The + -remote option requests the object be created on a remote + machine. The progID parameter is the programmatic + identifier of the object class. Use the -clsid option if + you want to specify the class using a class ID instead. The + hostName parameter specifies the machine where you + want to create the object instance.

+

The getactiveobject subcommand gets a reference + to an already existing object.

+
+ + +
+ + ::tcom::ref + getobject + pathName + +
+
+

This command returns a reference to a COM object from a file. The + pathName parameter is the full path and name of the + file containing the object.

+
+ + +
+ + ::tcom::ref + equal + handle1 + handle2 + +
+
+

This command compares the interface pointers represented by two + handles for COM identity, returning 1 if the interface pointers refer to + the same COM object, or 0 if not.

+
+ + +
+ + handle + ?-method? + method + ?argument ...? + +
+
+

This command invokes a method on the object represented by the + handle. The return value of the method is returned + as a Tcl value. A Tcl error will be raised if the method returns a + failure HRESULT code. Parameters with the [in] attribute are passed by + value. For each parameter with the [out] or [in, out] attributes, pass + the name of a Tcl variable as the argument. After the method returns, the + variables will contain the output values. In some cases where + tcom cannot get information about the object's + interface, you may have to use the -method option to + specify you want to invoke a method.

+
+ + +
+ + handle + -namedarg + method + ?argumentName argumentValue ...? + +
+
+

Use the -namedarg option to invoke a method + with named arguments. This only works with objects that implement + IDispatch. You specify arguments by passing name and value pairs.

+
+ + +
+ + handle + ?-get? + ?-set? + property + ?index ...? + ?value? + +
+
+

This command gets or sets a property of the object represented by + the handle. If you supply a + value argument, this command sets the named + property to the value, otherwise it returns the property value. For + indexed properties, you must specify one or more + index values. The command raises a Tcl error if + you specify an invalid property name or if you try to set a value that + cannot be converted to the property's type. In some cases where + tcom cannot get information about the object's + interface, you may have to use the -get or + -set option to specify you want to get or set a property + respectively.

+
+ + +
+ + ::tcom::foreach + varname + collectionHandle + body +
+ ::tcom::foreach + varlist + collectionHandle + body + +
+
+

This command implements a loop where the loop variable(s) take on + values from a collection object represented by + collectionHandle. In the simplest case, there + is one loop variable, varname. The + body argument is a Tcl script. For each + element of the collection, the command assigns the contents of the element + to varname, then calls the Tcl interpreter to + execute body.

+

In the general case, there can be more than one loop variable. + During each iteration of the loop, the variables of + varlist are assigned consecutive elements from + the collection. Each element is used exactly once. The total number of + loop iterations is large enough to use up all the elements from the + collection. On the last iteration, if the collection does not contain + enough elements for each of the loop variables, empty values are used for + the missing elements.

+

The break and continue + statements may be invoked inside body, with the + same effect as in the for command. The + ::tcom::foreach command returns an empty string.

+
+ + +
+ + ::tcom::bind + handle + command + ?eventIID? + +
+
+

This command specifies a Tcl command that will be executed when + events are received from an object. The + command will be called with additional + arguments: the event name and the event arguments. By default, the event + interface is the default event source interface of the object's class. + Use the eventIID parameter to specify the IID + of another event interface.

+
+ + +
+ + ::tcom::unbind + handle + +
+
+

This command tears down all event connections to the object that + were set up by the ::tcom::bind command.

+
+ + +
+ + ::tcom::na + +
+
+

Objects that implement the IDispatch interface allow some method + parameters to be optional. This command returns a token representing a + missing optional argument. In practice, you would pass this token as a + method argument in place of a missing optional argument.

+
+ + +
+ + ::tcom::info interface + handle + +
+
+

This command returns a handle representing a description of the + interface exposed by the object. The handle supports the following + commands.

+
+ +
+ + interfaceHandle + iid + +
+
+

This command returns an interface identifier code.

+
+ + +
+ + interfaceHandle + methods + +
+
+

This command returns a list of method descriptions for methods + defined in the interface. Each method description is a list. The + first element is the member ID. The second element is the return type. + The third element is the method name. The fourth element is a list of + parameter descriptions.

+
+ + +
+ + interfaceHandle + name + +
+
+

This command returns the interface's name.

+
+ + +
+ + interfaceHandle + properties + +
+
+

This command returns a list of property descriptions for + properties defined in the interface. Each property description is a + list. The first element is the member ID. The second element is the + property read/write mode. The third element is the property data type. + The fourth element is the property name. If the property is an indexed + property, there is a fifth element which is a list of parameter + descriptions.

+
+ +
+
+ + +
+ + ::tcom::configure + name + ?value? + +
+
+

This command sets and retrieves options for the package. If + name is supplied but no + value then the command returns the current + value of the given option. If one or more pairs of + name and value are + supplied, the command sets each of the named options to the corresponding + value; in this case the return value is an empty string.

+
+ +
+ + -concurrency + ?concurrencyModel? + +
+
+

This option sets the concurrency model, which can be + apartmentthreaded or multithreaded. + The default is apartmentthreaded. You must configure + this option before performing any COM operations such as getting a + reference to an object. After a COM operation has been done, changing + this option has no effect.

+
+ +
+
+ +
+ + +

Importing Type Library Information

+ + ::tcom::import + typeLibrary + ?namespace? + +

Use the ::tcom::import command to convert type + information from a type library into Tcl commands to access COM classes and + interfaces. The typeLibrary argument specifies a + type library file. By default, the commands are defined in a namespace named + after the type library, but you may specify another namespace by supplying a + namespace argument. This command returns the + library name stored in the type library file.

+ +

Commands

+
+ +
+ + class + ?-inproc? + ?-local? + ?-remote? + ?hostName? + +
+
+

For each class in the type library, + ::tcom::import defines a Tcl command with the same + name as the class. The class command creates an object of the class and + returns a handle representing an interface pointer to the object. The + command accepts an optional hostName argument + to specify the machine where you want to create the object. You can use + the returned handle to invoke methods and access properties of the + object. In practice, you should store this handle in a Tcl variable or + pass it as an argument to a Tcl command.

+
+ + +
+ + interface + handle + +
+
+

For each interface in the type library, + ::tcom::import defines a Tcl command with the same + name as the interface. The interface command queries the object + represented by handle for an interface pointer + to that specific interface. The command returns a handle representing + the interface pointer. You can use the returned handle to invoke methods + and access properties of the object. In practice, you should store this + handle in a Tcl variable or pass it as an argument to a Tcl + command.

+
+ +
+ + +

Enumerations

+

The ::tcom::import command generates a Tcl array + for each enumeration defined in the type library. The array name is the + enumeration name. To get an enumerator value, use an enumerator name as an + index into the array.

+ + + +

Tcl Value to VARIANT Mapping

+

Each Tcl value has two representations. A Tcl value has a string + representation and also has an internal representation that can be + manipulated more efficiently. For example, a Tcl list is represented as an + object that holds the list's string representation as well as an array of + pointers to the objects for each list element. The two representations are a + cache of each other and are computed lazily. That is, each representation is + only computed when necessary, is computed from the other representation, and, + once computed, is saved. In addition, a change in one representation + invalidates the other one. As an example, a Tcl program doing integer + calculations can operate directly on a variable's internal machine integer + representation without having to constantly convert between integers and + strings. Only when it needs a string representing the variable's value, say + to print it, will the program regenerate the string representation from the + integer. The internal representations built into Tcl include boolean, + integer and floating point types.

+

When invoking COM object methods, tcom tries to + convert each Tcl argument to the parameter type specified by the method + interface. For example, if a method accepts an int + parameter, tcom tries to convert the argument to that + type. If the parameter type is a VARIANT, the conversion has an extra + complication because a VARIANT is designed to hold many different data types. + One approach might be to simply copy the Tcl value's string representation + to a string in the VARIANT, and hope the method's implementation can correctly + interpret the string, but this doesn't work in general because some + implementations expect certain VARIANT types.

+

Tcom uses the Tcl value's internal representation + type as a hint to choose the resulting VARIANT type.

+
+ Tcl value to VARIANT mapping
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Tcl internal representationVARIANT type
booleanVT_BOOL
intVT_I4
doubleVT_R8
listone-dimensional array of VT_VARIANT
bytearrayone-dimensional array of VT_UI1
otherVT_BSTR
+
+ +

Invoking Methods With VARIANT Parameters

+

The internal representation of a Tcl value may become significant when + it is passed to a VARIANT parameter of a method. For example, the standard + interface for COM collections defines the Item method for + getting an element by specifying an index. Many implementations of the + method allow the index to be an integer value (usually based from 1) or a + string key. If the index parameter is a VARIANT, you must account for the + internal representation type of the Tcl argument passed to that + parameter.

+
+
+# Assume $collection is a handle to a collection.
+set element [$collection Item 1]
+
+

This command passes a string consisting of the single character "1" + to the Item method. The method may return an error because it can't find an + element with that string key.

+
+
+set numElements [$collection Count]
+for {set i 1} {$i <= $numElements} {incr i} {  ;# 1
+    set element [$collection Item $i]  ;# 2
+}
+
+

In line 1, the for command sets the internal + representation of $i to an int type as a side effect of + evaluating the condition expression {$i <= + $numElements}. The command in line 2 passes the integer value in + $i to the Item method, which should succeed if the method + can handle integer index values.

+ + + + diff --git a/doc/tcom.n.xml b/doc/tcom.n.xml new file mode 100644 index 0000000..f5c20e4 --- /dev/null +++ b/doc/tcom.n.xml @@ -0,0 +1,593 @@ + + + + + + $Date: 2002/04/12 23:44:50 $ + $Revision: 1.63 $ + + + tcom + n + + + tcom + Access COM objects from Tcl + + + + package require tcom + + + ::tcom::ref + createobject + + + + + progID + hostName + + ::tcom::ref + getactiveobject + + progID + + ::tcom::ref + getobject + pathName + + ::tcom::ref + equal + handle1 + handle2 + + handle + + method + argument + + handle + + method + argumentName argumentValue + + handle + + + property + index + value + + ::tcom::foreach + varname + collectionHandle + body + + ::tcom::foreach + varlist + collectionHandle + body + + ::tcom::bind + handle + command + eventIID + + ::tcom::unbind + handle + + ::tcom::na + + ::tcom::info interface + handle + + ::tcom::configure + name + value + + ::tcom::import + typeLibrary + namespace + + + + + Description + The tcom package provides commands to access COM + objects through IDispatch and IUnknown derived interfaces. + + + Commands + + + + + ::tcom::ref + createobject + + + + + progID + hostName + + ::tcom::ref + getactiveobject + + progID + + + + These commands return a handle representing a reference to a COM + object through an interface pointer. The handle can be used as a Tcl + command to invoke operations on the object. In practice, you should store + the handle in a Tcl variable or pass it as an argument to another command. + + References to COM objects are automatically released. If you store + the handle in a local variable, the reference is released when execution + leaves the variable's scope. If you store the handle in a global + variable, you can release the reference by unsetting the variable, setting + the variable to another value, or exiting the Tcl interpreter. + The createobject subcommand creates an instance + of the object. The option requests the object be + created in the same process. The option requests + the object be created in another process on the local machine. The + option requests the object be created on a remote + machine. The progID parameter is the programmatic + identifier of the object class. Use the option if + you want to specify the class using a class ID instead. The + hostName parameter specifies the machine where you + want to create the object instance. + The getactiveobject subcommand gets a reference + to an already existing object. + + + + + + ::tcom::ref + getobject + pathName + + + + This command returns a reference to a COM object from a file. The + pathName parameter is the full path and name of the + file containing the object. + + + + + + ::tcom::ref + equal + handle1 + handle2 + + + + This command compares the interface pointers represented by two + handles for COM identity, returning 1 if the interface pointers refer to + the same COM object, or 0 if not. + + + + + + handle + + method + argument + + + + This command invokes a method on the object represented by the + handle. The return value of the method is returned + as a Tcl value. A Tcl error will be raised if the method returns a + failure HRESULT code. Parameters with the [in] attribute are passed by + value. For each parameter with the [out] or [in, out] attributes, pass + the name of a Tcl variable as the argument. After the method returns, the + variables will contain the output values. In some cases where + tcom cannot get information about the object's + interface, you may have to use the option to + specify you want to invoke a method. + + + + + + handle + + method + argumentName argumentValue + + + + Use the option to invoke a method + with named arguments. This only works with objects that implement + IDispatch. You specify arguments by passing name and value pairs. + + + + + + handle + + + property + index + value + + + + This command gets or sets a property of the object represented by + the handle. If you supply a + value argument, this command sets the named + property to the value, otherwise it returns the property value. For + indexed properties, you must specify one or more + index values. The command raises a Tcl error if + you specify an invalid property name or if you try to set a value that + cannot be converted to the property's type. In some cases where + tcom cannot get information about the object's + interface, you may have to use the or + option to specify you want to get or set a property + respectively. + + + + + + ::tcom::foreach + varname + collectionHandle + body + + ::tcom::foreach + varlist + collectionHandle + body + + + + This command implements a loop where the loop variable(s) take on + values from a collection object represented by + collectionHandle. In the simplest case, there + is one loop variable, varname. The + body argument is a Tcl script. For each + element of the collection, the command assigns the contents of the element + to varname, then calls the Tcl interpreter to + execute body. + In the general case, there can be more than one loop variable. + During each iteration of the loop, the variables of + varlist are assigned consecutive elements from + the collection. Each element is used exactly once. The total number of + loop iterations is large enough to use up all the elements from the + collection. On the last iteration, if the collection does not contain + enough elements for each of the loop variables, empty values are used for + the missing elements. + The break and continue + statements may be invoked inside body, with the + same effect as in the for command. The + ::tcom::foreach command returns an empty string. + + + + + + ::tcom::bind + handle + command + eventIID + + + + This command specifies a Tcl command that will be executed when + events are received from an object. The + command will be called with additional + arguments: the event name and the event arguments. By default, the event + interface is the default event source interface of the object's class. + Use the eventIID parameter to specify the IID + of another event interface. + + + + + + ::tcom::unbind + handle + + + + This command tears down all event connections to the object that + were set up by the ::tcom::bind command. + + + + + + ::tcom::na + + + + Objects that implement the IDispatch interface allow some method + parameters to be optional. This command returns a token representing a + missing optional argument. In practice, you would pass this token as a + method argument in place of a missing optional argument. + + + + + + ::tcom::info interface + handle + + + + This command returns a handle representing a description of the + interface exposed by the object. The handle supports the following + commands. + + + + + interfaceHandle + iid + + + + This command returns an interface identifier code. + + + + + + interfaceHandle + methods + + + + This command returns a list of method descriptions for methods + defined in the interface. Each method description is a list. The + first element is the member ID. The second element is the return type. + The third element is the method name. The fourth element is a list of + parameter descriptions. + + + + + + interfaceHandle + name + + + + This command returns the interface's name. + + + + + + interfaceHandle + properties + + + + This command returns a list of property descriptions for + properties defined in the interface. Each property description is a + list. The first element is the member ID. The second element is the + property read/write mode. The third element is the property data type. + The fourth element is the property name. If the property is an indexed + property, there is a fifth element which is a list of parameter + descriptions. + + + + + + + + + ::tcom::configure + name + value + + + + This command sets and retrieves options for the package. If + name is supplied but no + value then the command returns the current + value of the given option. If one or more pairs of + name and value are + supplied, the command sets each of the named options to the corresponding + value; in this case the return value is an empty string. + + + + + + concurrencyModel + + + + This option sets the concurrency model, which can be + or . + The default is . You must configure + this option before performing any COM operations such as getting a + reference to an object. After a COM operation has been done, changing + this option has no effect. + + + + + + + + + Importing Type Library Information + + ::tcom::import + typeLibrary + namespace + + Use the ::tcom::import command to convert type + information from a type library into Tcl commands to access COM classes and + interfaces. The typeLibrary argument specifies a + type library file. By default, the commands are defined in a namespace named + after the type library, but you may specify another namespace by supplying a + namespace argument. This command returns the + library name stored in the type library file. + + Commands + + + + + class + + + + hostName + + + + For each class in the type library, + ::tcom::import defines a Tcl command with the same + name as the class. The class command creates an object of the class and + returns a handle representing an interface pointer to the object. The + command accepts an optional hostName argument + to specify the machine where you want to create the object. You can use + the returned handle to invoke methods and access properties of the + object. In practice, you should store this handle in a Tcl variable or + pass it as an argument to a Tcl command. + + + + + + interface + handle + + + + For each interface in the type library, + ::tcom::import defines a Tcl command with the same + name as the interface. The interface command queries the object + represented by handle for an interface pointer + to that specific interface. The command returns a handle representing + the interface pointer. You can use the returned handle to invoke methods + and access properties of the object. In practice, you should store this + handle in a Tcl variable or pass it as an argument to a Tcl + command. + + + + + + Enumerations + The ::tcom::import command generates a Tcl array + for each enumeration defined in the type library. The array name is the + enumeration name. To get an enumerator value, use an enumerator name as an + index into the array. + + + + Tcl Value to VARIANT Mapping + Each Tcl value has two representations. A Tcl value has a string + representation and also has an internal representation that can be + manipulated more efficiently. For example, a Tcl list is represented as an + object that holds the list's string representation as well as an array of + pointers to the objects for each list element. The two representations are a + cache of each other and are computed lazily. That is, each representation is + only computed when necessary, is computed from the other representation, and, + once computed, is saved. In addition, a change in one representation + invalidates the other one. As an example, a Tcl program doing integer + calculations can operate directly on a variable's internal machine integer + representation without having to constantly convert between integers and + strings. Only when it needs a string representing the variable's value, say + to print it, will the program regenerate the string representation from the + integer. The internal representations built into Tcl include boolean, + integer and floating point types. + When invoking COM object methods, tcom tries to + convert each Tcl argument to the parameter type specified by the method + interface. For example, if a method accepts an int + parameter, tcom tries to convert the argument to that + type. If the parameter type is a VARIANT, the conversion has an extra + complication because a VARIANT is designed to hold many different data types. + One approach might be to simply copy the Tcl value's string representation + to a string in the VARIANT, and hope the method's implementation can correctly + interpret the string, but this doesn't work in general because some + implementations expect certain VARIANT types. + Tcom uses the Tcl value's internal representation + type as a hint to choose the resulting VARIANT type. + + Tcl value to VARIANT mapping + + + + Tcl internal representation + VARIANT type + + + + + boolean + VT_BOOL + + + int + VT_I4 + + + double + VT_R8 + + + list + one-dimensional array of VT_VARIANT + + + bytearray + one-dimensional array of VT_UI1 + + + other + VT_BSTR + + + +
+ + Invoking Methods With VARIANT Parameters + The internal representation of a Tcl value may become significant when + it is passed to a VARIANT parameter of a method. For example, the standard + interface for COM collections defines the Item method for + getting an element by specifying an index. Many implementations of the + method allow the index to be an integer value (usually based from 1) or a + string key. If the index parameter is a VARIANT, you must account for the + internal representation type of the Tcl argument passed to that + parameter. + + +# Assume $collection is a handle to a collection. +set element [$collection Item 1] + + This command passes a string consisting of the single character "1" + to the Item method. The method may return an error because it can't find an + element with that string key. + + +set numElements [$collection Count] +for {set i 1} {$i <= $numElements} {incr i} { ;# 1 + set element [$collection Item $i] ;# 2 +} + + In line 1, the for command sets the internal + representation of $i to an int type as a side effect of + evaluating the condition expression {$i <= + $numElements}. The command in line 2 passes the integer value in + $i to the Item method, which should succeed if the method + can handle integer index values. + +
+
diff --git a/doc/xslt.tcl b/doc/xslt.tcl new file mode 100644 index 0000000..4df74fa --- /dev/null +++ b/doc/xslt.tcl @@ -0,0 +1,47 @@ +# $Id: xslt.tcl,v 1.1 2002/04/17 22:07:57 cthuang Exp $ +# +# Run an XML document through an XSLT processor. + +if {$argc != 3} { + puts "usage: $argv0 inputFile xsltFile outputFile" + exit 1 +} + +package require tcom + +set domProgId "Msxml2.DOMDocument" + +set source [::tcom::ref createobject $domProgId] +$source preserveWhiteSpace 1 +$source validateOnParse 0 +set sourceUrl [lindex $argv 0] +if {![$source load $sourceUrl]} { + set parseError [$source parseError] + puts [format "%x" [$parseError errorCode]] + puts [$parseError reason] + puts [$parseError srcText] + puts [$parseError url] + exit 1 +} + +set xslt [::tcom::ref createobject $domProgId] +$xslt preserveWhiteSpace 1 +$xslt validateOnParse 0 +set xsltUrl [lindex $argv 1] +if {![$xslt load $xsltUrl]} { + set parseError [$xslt parseError] + puts [format "%x" [$parseError errorCode]] + puts [$parseError reason] + puts [$parseError srcText] + puts [$parseError url] + exit 1 +} + +regsub {]*>} [$source transformNode $xslt] \ + {} \ + resultHtml + +set out [open [lindex $argv 2] "w"] +fconfigure $out -translation binary +puts -nonewline $out $resultHtml +close $out diff --git a/lib/Banking/Banking.tlb b/lib/Banking/Banking.tlb new file mode 100644 index 0000000000000000000000000000000000000000..83694deeb579b67f55610f6eaf2807db21114a6b GIT binary patch literal 2396 zcmcgu&r4KM6h1Sf&WvR`=|UypAqf(bOwnQpgYgHFU}QCT3tae|dF9YJ^O$*Jf{=?@ zwr~;ECXi|oBw^4ZT)8lCTM&ecn4nGnK(xxv_ujpFeK{sAI&kK^^PTUUd(NHv?#z`N zBk3Ro2okMx4GpmI5F!bA0scJBDx|l=0^eX=VnvS1%kuLUYn*{J>oP0&&5TWQQRFFq zhW%5T-_O3G`QL(GeZ|j)h(2ra3iF9MDqd+I`l-bi!$fPEACK&>k30_V=8ti_Me`@w zZ`XW3-|zQy6FHm%L5$n2DVI6Ity7z1U>}qqm$&VY?*RCP?tr^SiV4^YwT7~)2JA9x zDn`^R!MLJ!xUQHM}{nuS&c*Lhc&l z3}P=-|LDap8w_BMH5sUA1aA%Fz&Q{D6+PkkUuCLBe!_=iFE%8GbMx*NHLg`Z#vY!b zjjML*)i2pVW_|w4;SQJEtG~L%IKC4fe--caGULcoHBlG1?z889o!Iv&RtS1KOJyfp%vnz|s)N35|c~i?9*Pf4>|37%uL;C7HBl$(GD{ya%XPm;CV>{o$O$uTKVM zM*;8|R#+3gxj0m^Ovh4Pd*>VcC~ZHs?LEAm~c+g7^%sq2yut@MHd^+Mq8gJAXqTW}#2R{REa zP{$G*{_P*5*zn$ehZH~-yhn|!alTa)*PvnkpAvBI8F139=n3yL?w&Z`Fp7KH<*I)- s-hHKW!N+}sOA7Z2X2P8TL4P-;-QR1PqFS#{n1ea1tmqXrH(4RS0hu!iw*UYD literal 0 HcmV?d00001 diff --git a/lib/Banking/pkgIndex.tcl b/lib/Banking/pkgIndex.tcl new file mode 100644 index 0000000..1c3601c --- /dev/null +++ b/lib/Banking/pkgIndex.tcl @@ -0,0 +1,2 @@ +# $Id: pkgIndex.tcl,v 1.3 2001/07/04 03:36:16 cthuang Exp $ +package ifneeded Banking 1.0 [list source [file join $dir server.tcl]] diff --git a/lib/Banking/server.itcl b/lib/Banking/server.itcl new file mode 100644 index 0000000..7b9540d --- /dev/null +++ b/lib/Banking/server.itcl @@ -0,0 +1,34 @@ +# $Id: server.itcl,v 1.7 2002/06/29 15:34:52 cthuang Exp $ +package provide Banking 1.0 + +package require Itcl +namespace import ::itcl::* + +package require tcom +::tcom::import [file join [file dirname [info script]] Banking.tlb] + +class AccountImpl { + private variable balance 0 + + public method _get_Balance {} { + return $balance + } + + public method Deposit {amount} { + set balance [expr $balance + $amount] + } + + public method Withdraw {amount} { + set balance [expr $balance - $amount] + } +} + +class BankImpl { + public method CreateAccount {} { + set accountImpl [AccountImpl #auto] + return [::tcom::object create ::Banking::Account \ + [code $accountImpl] {delete object}] + } +} + +::tcom::object registerfactory ::Banking::Bank {BankImpl #auto} {delete object} diff --git a/lib/Banking/server.tcl b/lib/Banking/server.tcl new file mode 100644 index 0000000..520e669 --- /dev/null +++ b/lib/Banking/server.tcl @@ -0,0 +1,47 @@ +# $Id: server.tcl,v 1.3 2002/06/29 15:34:52 cthuang Exp $ +package provide Banking 1.0 + +package require tcom +::tcom::import [file join [file dirname [info script]] Banking.tlb] + +proc accountImpl {method args} { + global balance + + switch -- $method { + _get_Balance { + return $balance + } + + Deposit { + set amount [lindex $args 0] + set balance [expr $balance + $amount] + } + + Withdraw { + set amount [lindex $args 0] + set balance [expr $balance - $amount] + } + + default { + error "unknown method $method $args" + } + } +} + +proc bankImpl {method args} { + global balance + + switch -- $method { + CreateAccount { + set balance 0 + set name "" + return [::tcom::object create ::Banking::Account accountImpl] + } + + default { + error "unknown method $method $args" + } + } +} + +::tcom::object registerfactory ::Banking::Bank {list bankImpl} diff --git a/lib/TclScript/TclScript.dll b/lib/TclScript/TclScript.dll new file mode 100644 index 0000000000000000000000000000000000000000..81409ac4f4f83e66ee0260c452e1e9a4eb353060 GIT binary patch literal 32833 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 literal 0 HcmV?d00001 diff --git a/lib/TclScript/TclScript.itcl b/lib/TclScript/TclScript.itcl new file mode 100644 index 0000000..40c93a3 --- /dev/null +++ b/lib/TclScript/TclScript.itcl @@ -0,0 +1,422 @@ +# $Id: TclScript.itcl,v 1.2 2002/04/20 06:11:32 cthuang Exp $ + +package require Itcl +namespace import itcl::* + +package require tcom +::tcom::import [file join [file dirname [info script]] TclScript.tlb] + +class Engine { + # common HRESULT values + common E_NOTIMPL 0x80004001 + common E_FAIL 0x80004005 + + # engine states + common SCRIPTSTATE_UNINITIALIZED 0 + common SCRIPTSTATE_INITIALIZED 5 + common SCRIPTSTATE_STARTED 1 + common SCRIPTSTATE_CONNECTED 2 + common SCRIPTSTATE_DISCONNECTED 3 + common SCRIPTSTATE_CLOSED 4 + + # map script state code to description + common scriptStateDesc + array set scriptStateDesc { + 0 SCRIPTSTATE_UNINITIALIZED + 5 SCRIPTSTATE_INITIALIZED + 1 SCRIPTSTATE_STARTED + 2 SCRIPTSTATE_CONNECTED + 3 SCRIPTSTATE_DISCONNECTED + 4 SCRIPTSTATE_CLOSED + } + + # flags passed into AddNamedItem method + common SCRIPTITEM_ISVISIBLE 2 + common SCRIPTITEM_ISSOURCE 4 + common SCRIPTITEM_GLOBALMEMBERS 8 + common SCRIPTITEM_ISPERSISTENT 0x40 + common SCRIPTITEM_CODEONLY 0x200 + common SCRIPTITEM_NOCODE 0x400 + + # true if logging to debug output enabled + variable logDebugOn_ 1 + + # SCRIPTSTATE + variable scriptState_ + + # IActiveScriptSite + variable scriptSite_ + + # slave interpreter used to execute scripts + variable slave_ + + # code to execute + variable code_ {} + + # list of names of items which have global members + variable globalMemberItems_ {} + + # list of names of item commands added to the Tcl interpreter + variable itemCommands_ {} + + # array of scripts to execute for each event + variable eventCode_ + array set eventCode_ {} + + # array of item and sub-item names of event sources + variable eventSources_ + array set eventSources_ {} + + # array of connected event sources + variable connectedSources_ + array set connectedSources_ {} + + constructor {} { + set scriptState_ $SCRIPTSTATE_UNINITIALIZED + + log "Engine::constructor" + } + + destructor { + log "Engine::destructor" + } + + method createItemCommand {itemName unknown} { + log "createItemCommand $itemName $unknown" + + $slave_ alias ::$itemName $unknown + lappend itemCommands_ $itemName + } + + method resolveUnknownCommand {args} { + log "resolveUnknownCommand $args" + + # See if any named items have a sub-item with that name. + set subItemName [lindex $args 0] + foreach itemName $globalMemberItems_ { + set obj [::TclScriptEngine::getnameditem \ + $scriptSite_ $itemName $subItemName] + if {[string equal [::tcom::typeof $obj] cmdName]} { + createItemCommand $subItemName $obj + return [eval $obj [lrange $args 1 end]] + } + } + + # Fall back to original unknown. + eval unknown $args + } + + method log {msg} { + if {$logDebugOn_} { + ::TclScriptEngine::outputdebug $msg + } + } + + method dumpInterface {obj} { + set interface [::tcom::info interface $obj] + log "interface [$interface name]" + + set properties [$interface properties] + foreach property $properties { + log "property $property" + } + + set methods [$interface methods] + foreach method $methods { + log "method [lrange $method 0 2] \{" + set parameters [lindex $method 3] + foreach parameter $parameters { + log " \{$parameter\}" + } + log "\}" + } + } + + method evaluateCode {code} { + $scriptSite_ OnEnterScript + if {[catch {$slave_ eval $code} result]} { + log $::errorInfo + set error [::TclScriptEngine::activescripterror \ + $E_FAIL TclScript $result 0 0 $::errorInfo] + $scriptSite_ OnScriptError $error + } + $scriptSite_ OnLeaveScript + } + + method changeScriptState {newState} { + set scriptState_ $newState + if {[info exists scriptSite_]} { + $scriptSite_ OnStateChange $newState + } + + switch -- $newState \ + $SCRIPTSTATE_STARTED { + evaluateCode $code_ + set code_ {} + } + } + + method sink {sourceName eventName} { + if {[info exists eventCode_($sourceName,$eventName)]} { + $slave_ eval $eventCode_($sourceName,$eventName) + } + } + + method connectToSources {} { + foreach sourceName [array names eventSources_] { + # Check if the source is already connected to a sink. + if {![info exists connectedSources_($sourceName)]} { + set itemName [lindex $eventSources_($sourceName) 0] + set subItemName [lindex $eventSources_($sourceName) 1] + set source [::TclScriptEngine::getnameditem \ + $scriptSite_ $itemName $subItemName] + + set sinkProcName ::${sourceName}_sink + proc $sinkProcName {eventName args} \ + "$this sink $sourceName \$eventName" + ::tcom::bind $source $sinkProcName + + set connectedSources_($sourceName) $source + } + } + } + + method disconnectFromSources {} { + foreach {sourceName source} [array get connectedSources_] { + ::tcom::unbind $source + unset connectedSources_($sourceName) + } + } + + # Raise not implemented error. + method errorNotImpl {} { + set messageText "Not implemented" + error $messageText {} [list COM $E_NOTIMPL $messageText] + } + + # IActiveScript implementation + + method SetScriptSite {site} { + log "IActiveScript::SetScriptSite $site" + + set scriptSite_ $site + } + + method GetScriptSite {iid ppvObject} { + log "IActiveScript::GetScriptSite $iid" + + upvar $ppvObject pvObject + set pvObject $scriptSite_ + } + + method SetScriptState {newState} { + log "IActiveScript::SetScriptState $scriptStateDesc($newState)" + + switch -- $newState \ + $SCRIPTSTATE_STARTED { + if {$scriptState_ != $SCRIPTSTATE_INITIALIZED} { + error "must be in INITIALIZED state to go to STARTED state" + } + } \ + $SCRIPTSTATE_CONNECTED { + connectToSources + } \ + $SCRIPTSTATE_DISCONNECTED { + disconnectFromSources + } + + if {$newState != $scriptState_} { + changeScriptState $newState + } + } + + method GetScriptState {pState} { + log "IActiveScript::GetScriptState" + + upvar $pState state + set state $scriptState_ + } + + method Close {} { + log "IActiveScript::Close" + + changeScriptState $SCRIPTSTATE_CLOSED + + # Clear object references. + foreach itemName $itemCommands_ { + log "delete command $itemName" + $slave_ alias ::$itemName {} + } + + set eventSources_ {} + set scriptSite_ {} + unset scriptSite_ + + interp delete $slave_ + log "IActiveScript::Close done" + } + + method AddNamedItem {name flags} { + log "IActiveScript::AddNamedItem $name $flags" + + set unknown [::TclScriptEngine::getnameditem $scriptSite_ $name] + + if {($flags & $SCRIPTITEM_GLOBALMEMBERS) != 0} { + lappend globalMemberItems_ $name + } + + if {($flags & $SCRIPTITEM_ISVISIBLE) != 0} { + log "IActiveScript::AddNamedItem createItemCommand" + createItemCommand $name $unknown + } + } + + method AddTypeLib {libid major minor flags} { + log "IActiveScript::AddTypeLib" + errorNotImpl + } + + method GetScriptDispatch {itemName ppDispatch} { + log "IActiveScript::GetScriptDispatch $itemName" + upvar $ppDispatch pDispatch + set pDispatch 0 + errorNotImpl + } + + method GetCurrentScriptThreadID {pScriptThreadId} { + log "IActiveScript::GetCurrentScriptThreadID" + upvar $pScriptThreadId scriptThreadId + set scriptThreadId 0 + errorNotImpl + } + + method GetScriptThreadID {win32ThreadId pScriptThreadId} { + log "IActiveScript::GetScriptThreadID" + upvar $pScriptThreadId scriptThreadId + set scriptThreadId 0 + errorNotImpl + } + + method GetScriptThreadState {scriptThreadId pScriptThreadState} { + log "IActiveScript::GetScriptThreadState" + errorNotImpl + } + + method InterruptScriptThread {scriptThreadId excepInfo flags} { + log "IActiveScript::InterruptScriptThread" + errorNotImpl + } + + method Clone {ppScript} { + log "IActiveScript::Clone" + upvar $ppScript pScript + set pScript 0 + errorNotImpl + } + + # IActiveScriptParse implementation + + method InitNew {} { + log "IActiveScriptParse::InitNew" + + if {$safetyOptions & $INTERFACESAFE_FOR_UNTRUSTED_DATA} { + set slave_ [interp create -safe] + } else { + set slave_ [interp create] + } + $slave_ alias unknown $this resolveUnknownCommand + + changeScriptState $SCRIPTSTATE_INITIALIZED + } + + method AddScriptlet { + defaultName code itemName subItemName eventName delimiter + sourceContextCookie startingLineNumber flags pName pExcepInfo + } { + log "IActiveScriptParse::AddScriptlet $defaultName" + log "code $code" + log "itemName $itemName" + log "subItemName $subItemName" + log "eventName $eventName" + + set sourceName $itemName + if {[string length $subItemName] > 0} { + append sourceName _ $subItemName + } + + set eventSources_($sourceName) [list $itemName $subItemName] + set eventCode_($sourceName,$eventName) $code + connectToSources + + upvar $pName name + set name $sourceName + } + + method ParseScriptText { + code itemName pContext delimiter + sourceContextCookie startingLineNumber flags pVarResult pExcepInfo + } { + set code [string map { \r\n \n } $code] + log "IActiveScriptParse::ParseScriptText $code" + log "itemName $itemName" + log "flags $flags" + + switch -- $scriptState_ \ + $SCRIPTSTATE_INITIALIZED { + append code_ $code + } \ + $SCRIPTSTATE_STARTED - \ + $SCRIPTSTATE_CONNECTED - \ + $SCRIPTSTATE_DISCONNECTED { + evaluateCode $code + } \ + default { + error "invalid script state $scriptState_" + } + } + + # IObjectSafety implementation + + # option flags + common INTERFACESAFE_FOR_UNTRUSTED_CALLER 1 + common INTERFACESAFE_FOR_UNTRUSTED_DATA 2 + common INTERFACE_USES_DISPEX 4 + common INTERFACE_USES_SECURITY_MANAGER 8 + + # Internet Explorer seems to insist we say we support all the options + # even though we refuse to accept some. + common SUPPORTED_SAFETY_OPTIONS [expr \ + $INTERFACESAFE_FOR_UNTRUSTED_CALLER | \ + $INTERFACESAFE_FOR_UNTRUSTED_DATA | \ + $INTERFACE_USES_DISPEX | \ + $INTERFACE_USES_SECURITY_MANAGER] + + # currently set safety options + variable safetyOptions 0 + + method GetInterfaceSafetyOptions {iid pSupportedOptions pEnabledOptions} { + log "GetInterfaceSafetyOptions $iid" + + upvar $pSupportedOptions supportedOptions + upvar $pEnabledOptions enabledOptions + set supportedOptions $SUPPORTED_SAFETY_OPTIONS + set enabledOptions $safetyOptions + } + + method SetInterfaceSafetyOptions {iid optionSetMask enabledOptions} { + log "SetInterfaceSafetyOptions $iid $optionSetMask $enabledOptions" + + # Check optionSetMask for options we don't support. + if {$optionSetMask & ~$SUPPORTED_SAFETY_OPTIONS} { + error "tried to set unsupported option" + } + + set safetyOptions [expr ($safetyOptions & ~$optionSetMask) | \ + ($enabledOptions & $optionSetMask)] + + log "safetyOptions $safetyOptions" + } +} + +::tcom::object registerfactory ::TclScript::Engine \ + {Engine #auto} {delete object} diff --git a/lib/TclScript/TclScript.tlb b/lib/TclScript/TclScript.tlb new file mode 100644 index 0000000000000000000000000000000000000000..af2393780a5addb48e573eca772d1be0fbe7bd1c GIT binary patch literal 20252 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 literal 0 HcmV?d00001 diff --git a/lib/TclScript/pkgIndex.tcl b/lib/TclScript/pkgIndex.tcl new file mode 100644 index 0000000..74d19b9 --- /dev/null +++ b/lib/TclScript/pkgIndex.tcl @@ -0,0 +1,3 @@ +# $Id: pkgIndex.tcl,v 1.2 2002/03/30 18:49:10 cthuang Exp $ +package ifneeded TclScript 1.0 \ +[list load [file join $dir TclScript.dll]]\n[list source [file join $dir TclScript.itcl]] diff --git a/lib/TclScript/register.tcl b/lib/TclScript/register.tcl new file mode 100644 index 0000000..807ee29 --- /dev/null +++ b/lib/TclScript/register.tcl @@ -0,0 +1,34 @@ +# $Id: register.tcl,v 1.3 2002/03/20 23:52:35 cthuang Exp $ +# +# This script registers the Tcl Active Scripting engine. + +package require registry +package require tcom + + set typeLibFile "TclScript.tlb" + ::tcom::server register -inproc $typeLibFile + + set typeLib [::tcom::typelib load $typeLibFile] + set classInfo [$typeLib class "Engine"] + set clsid "{[string toupper [lindex $classInfo 0]]}" + set progId "TclScript" + + set key "HKEY_CLASSES_ROOT\\CLSID\\$clsid" + registry set "$key\\ProgID" "" $progId + registry set "$key\\OLEScript" + + set key "HKEY_CLASSES_ROOT\\CLSID\\$clsid\\Implemented Categories" + registry set "$key\\{F0B7A1A1-9847-11CF-8F20-00805F2CD064}" + registry set "$key\\{F0B7A1A2-9847-11CF-8F20-00805F2CD064}" + + set key "HKEY_CLASSES_ROOT\\$progId" + registry set $key "" "Tcl Script Language" + registry set "$key\\CLSID" "" $clsid + registry set "$key\\OLEScript" + + set key "HKEY_CLASSES_ROOT\\.tcls" + registry set $key "" "TclScriptFile" + + set key "HKEY_CLASSES_ROOT\\TclScriptFile" + registry set $key "" "Tcl Script File" + registry set "$key\\ScriptEngine" "" $progId diff --git a/lib/tcom/pkgIndex.tcl b/lib/tcom/pkgIndex.tcl new file mode 100644 index 0000000..bbfa714 --- /dev/null +++ b/lib/tcom/pkgIndex.tcl @@ -0,0 +1,3 @@ +# $Id: pkgIndex.tcl,v 1.15 2002/02/26 23:10:47 cthuang Exp $ +package ifneeded tcom 3.8 \ +[list load [file join $dir tcom.dll]]\n[list source [file join $dir tcom.tcl]] diff --git a/lib/tcom/tcom.dll b/lib/tcom/tcom.dll new file mode 100644 index 0000000000000000000000000000000000000000..c54312196401c5da058ce644432fc3d6ec2e7312 GIT binary patch literal 262194 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�W)=(z4{Zgr&&^QR_UFi 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 literal 0 HcmV?d00001 diff --git a/lib/tcom/tcom.tcl b/lib/tcom/tcom.tcl new file mode 100644 index 0000000..2044e33 --- /dev/null +++ b/lib/tcom/tcom.tcl @@ -0,0 +1,152 @@ +# $Id: tcom.tcl,v 1.14 2002/03/30 16:24:11 cthuang Exp $ + +namespace eval ::tcom { + # Look for the file in the directories in the package load path. + # Return the full path of the file. + proc search_auto_path {fileSpec} { + global auto_path + + ::foreach dir [set auto_path] { + set filePath [file join $dir $fileSpec] + if {[file exists $filePath]} { + return [file nativename $filePath] + } + } + error "cannot find $fileSpec" + } + + # Get full path to Tcl interpreter DLL. + proc tclDllPath {} { + set parts [file split [::info library]] + set n [expr [llength $parts] - 3] + set rootDir [eval file join [lrange $parts 0 $n]] + set version [string map {. {}} [::info tclversion]] + return [file nativename [file join $rootDir "bin" "tcl$version.dll"]] + } + + # Insert registry entries for the class. + proc registerClass { + typeLibName typeLibId version className clsid inproc local + } { + set dllPath [search_auto_path "tcom/tcominproc.dll"] + set exePath [search_auto_path "tcom/tcomlocal.exe"] + if {[string first " " $exePath] > 0} { + # Must not have space character in local server path name. + set exePath [::tcom::shortPathName $exePath] + } + set verIndependentProgId "$typeLibName.$className" + set progId "$verIndependentProgId.1" + + set key "HKEY_CLASSES_ROOT\\$progId" + registry set $key "" "$className Class" + registry set "$key\\CLSID" "" $clsid + + set key "HKEY_CLASSES_ROOT\\$verIndependentProgId" + registry set $key "" "$className Class" + registry set "$key\\CLSID" "" $clsid + + set key "HKEY_CLASSES_ROOT\\CLSID\\$clsid" + registry set $key "" "$className Class" + registry set "$key\\ProgID" "" $progId + registry set "$key\\VersionIndependentProgID" "" $verIndependentProgId + registry set "$key\\TypeLib" "" $typeLibId + registry set "$key\\Version" "" $version + + if {$inproc} { + set key "HKEY_CLASSES_ROOT\\CLSID\\$clsid\\InprocServer32" + registry set $key "" $dllPath + registry set $key "ThreadingModel" "Apartment" + } + + if {$local} { + set key "HKEY_CLASSES_ROOT\\CLSID\\$clsid\\LocalServer32" + registry set $key "" "$exePath $clsid" + } + + set key "HKEY_CLASSES_ROOT\\CLSID\\$clsid\\tcom" + registry set $key "Script" "package require $typeLibName" + registry set $key "TclDLL" [tclDllPath] + } + + # Remove registry entries for the class. + proc unregisterClass {typeLibName className clsid} { + set verIndependentProgId "$typeLibName.$className" + set progId "$verIndependentProgId.1" + + registry delete "HKEY_CLASSES_ROOT\\$progId" + registry delete "HKEY_CLASSES_ROOT\\$verIndependentProgId" + registry delete "HKEY_CLASSES_ROOT\\CLSID\\$clsid" + } + + # Register or unregister servers for classes defined in a type library. + proc server {subCommand args} { + package require registry + + set inproc 1 + set local 1 + + set argc [llength $args] + for {set i 0} {$i < $argc} {incr i} { + set endOfOptions 0 + switch -- [lindex $args $i] { + -inproc { + set inproc 1 + set local 0 + } + -local { + set inproc 0 + set local 1 + } + default { + set endOfOptions 1 + } + } + if {$endOfOptions} { + break + } + } + + if {$i >= $argc} { + error "wrong # args: usage: ::tcom::server register|unregister typeLibFile ?class ...?" + } + + set typeLibFile [lindex $args $i] + incr i + + switch -- $subCommand { + register { + ::tcom::typelib register $typeLibFile + set registerOpt 1 + } + unregister { + ::tcom::typelib unregister $typeLibFile + set registerOpt 0 + } + default { + error "bad option $option: must be register or unregsiter" + } + } + + set typeLib [::tcom::typelib load $typeLibFile] + set typeLibName [$typeLib name] + set typeLibId "{[string toupper [$typeLib libid]]}" + set typeLibVersion [$typeLib version] + + if {$i < $argc} { + set classes [lrange $args $i end] + } else { + set classes [$typeLib class] + } + + ::foreach className $classes { + set classInfo [$typeLib class $className] + set clsid "{[string toupper [lindex $classInfo 0]]}" + if {$registerOpt} { + registerClass $typeLibName $typeLibId $typeLibVersion \ + $className $clsid $inproc $local + } else { + unregisterClass $typeLibName $className $clsid + } + } + } +} diff --git a/lib/tcom/tcominproc.dll b/lib/tcom/tcominproc.dll new file mode 100644 index 0000000000000000000000000000000000000000..ddedbfe09a9d270954d78f074c6024f948e743a1 GIT binary patch literal 32834 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 literal 0 HcmV?d00001 diff --git a/lib/tcom/tcomlocal.exe b/lib/tcom/tcomlocal.exe new file mode 100644 index 0000000000000000000000000000000000000000..f71826876875e4a554f8baa1f37ec917ad40f354 GIT binary patch literal 32833 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 literal 0 HcmV?d00001 diff --git a/samples/Banking/Banking.idl b/samples/Banking/Banking.idl new file mode 100644 index 0000000..71e35a0 --- /dev/null +++ b/samples/Banking/Banking.idl @@ -0,0 +1,62 @@ +import "oaidl.idl"; +import "ocidl.idl"; + + [ + object, + uuid(0A0059C4-E0B0-11D2-942A-00C04F7040AB), + dual, + helpstring("IAccount Interface"), + pointer_default(unique) + ] + interface IAccount: IDispatch + { + [id(1), propget, helpstring("property Balance")] + HRESULT Balance([out, retval] long *pValue); + + [id(2), helpstring("method Deposit")] + HRESULT Deposit([in] long amount); + + [id(3), helpstring("method Withdraw")] + HRESULT Withdraw([in] long amount); + }; + + [ + object, + uuid(0A0059C4-E0B0-11D2-942A-00C04F7040AC), + dual, + helpstring("IBank Interface"), + pointer_default(unique) + ] + interface IBank: IDispatch + { + [id(1), helpstring("method CreateAccount")] + HRESULT CreateAccount([out, retval] IAccount **ppAccount); + }; + +[ + uuid(0A0059B8-E0B0-11D2-942A-00C04F7040AB), + version(1.0), + helpstring("Banking 1.0 Type Library") +] +library Banking +{ + importlib("stdole32.tlb"); + + [ + uuid(0A0059C5-E0B0-11D2-942A-00C04F7040AB), + helpstring("Account Class") + ] + coclass Account + { + [default] interface IAccount; + }; + + [ + uuid(0A0059C5-E0B0-11D2-942A-00C04F7040AC), + helpstring("Bank Class") + ] + coclass Bank + { + [default] interface IBank; + }; +}; diff --git a/samples/Banking/client.tcl b/samples/Banking/client.tcl new file mode 100644 index 0000000..0f975e6 --- /dev/null +++ b/samples/Banking/client.tcl @@ -0,0 +1,9 @@ +package require tcom + +set bank [::tcom::ref createobject "Banking.Bank"] +set account [$bank CreateAccount] +puts [$account Balance] +$account Deposit 20 +puts [$account Balance] +$account Withdraw 10 +puts [$account Balance] diff --git a/samples/chart.tcl b/samples/chart.tcl new file mode 100644 index 0000000..cb34c09 --- /dev/null +++ b/samples/chart.tcl @@ -0,0 +1,43 @@ +# $Id: chart.tcl,v 1.1 2001/08/18 00:35:52 cthuang Exp $ +# +# This example controls Excel. It performs the following steps. +# - Start Excel application. +# - Create a new workbook. +# - Put values into some cells. +# - Create a chart. + +package require tcom + +set application [::tcom::ref createobject "Excel.Application"] +$application Visible 1 + +set workbooks [$application Workbooks] +set workbook [$workbooks Add] +set worksheets [$workbook Worksheets] +set worksheet [$worksheets Item [expr 1]] + +set cells [$worksheet Cells] +$cells Item 1 A "North" +$cells Item 1 B "South" +$cells Item 1 C "East" +$cells Item 1 D "West" +$cells Item 2 A 5.2 +$cells Item 2 B 10.0 +$cells Item 2 C 8.0 +$cells Item 2 D 20.0 +set sourceRange [$worksheet Range "A1" "D2"] + +set charts [$workbook Charts] +set chart [$charts Add] +$chart ChartWizard \ + $sourceRange \ + [expr -4102] \ + [expr 7] \ + [expr 1] \ + [expr 1] \ + [expr 0] \ + 0 \ + "Sales Percentages" + +# Prevent Excel from prompting to save the document on close. +$workbook Saved 1 diff --git a/samples/events.tcl b/samples/events.tcl new file mode 100644 index 0000000..32e02d3 --- /dev/null +++ b/samples/events.tcl @@ -0,0 +1,20 @@ +# $Id: events.tcl,v 1.2 2001/06/30 18:42:58 cthuang Exp $ + +package require tcom + +proc sink {method args} { + puts "event $method $args" +} + +proc doUpdate {comment} { + puts "invoked $comment" + update +} + +set application [::tcom::ref createobject "InternetExplorer.Application"] +::tcom::bind $application sink + +$application Visible 1 +doUpdate "Visible" +$application Quit +doUpdate "Quit" diff --git a/samples/excel.tcl b/samples/excel.tcl new file mode 100644 index 0000000..4bc3031 --- /dev/null +++ b/samples/excel.tcl @@ -0,0 +1,50 @@ +# $Id: excel.tcl,v 1.9 2001/06/30 18:42:58 cthuang Exp $ +# +# This example controls Excel. It performs the following steps. +# - Start Excel application. +# - Create a new workbook. +# - Put values into some cells. +# - Save the workbook to a file. +# - Exit Excel application. + +package require tcom + +# Print the properties and methods exposed by the object. + +proc dumpInterface {obj} { + set interface [::tcom::info interface $obj] + + set properties [$interface properties] + foreach property $properties { + puts "property $property" + } + + set methods [$interface methods] + foreach method $methods { + puts "method [lrange $method 0 2] \{" + set parameters [lindex $method 3] + foreach parameter $parameters { + puts " \{$parameter\}" + } + puts "\}" + } +} + +set application [::tcom::ref createobject "Excel.Application"] +$application Visible 1 + +set workbooks [$application Workbooks] +set workbook [$workbooks Add] +set worksheets [$workbook Worksheets] +set worksheet [$worksheets Item [expr 1]] + +set cells [$worksheet Cells] +set i 0 +foreach row {1 2 3} { + foreach column {A B C} { + $cells Item $row $column [incr i] + } +} + +$workbook SaveAs {c:\tst.xls} +$application Quit diff --git a/samples/sendkeys.tcl b/samples/sendkeys.tcl new file mode 100644 index 0000000..e2705ab --- /dev/null +++ b/samples/sendkeys.tcl @@ -0,0 +1,13 @@ +# $Id: sendkeys.tcl,v 1.3 2001/06/30 18:42:58 cthuang Exp $ +# +# This example demonstrates how to send keys to Windows applications. +# It requires Windows Script Host 2.0 installed on the system. + +package require tcom + +set wshShell [::tcom::ref createobject "WScript.Shell"] +set taskId [$wshShell Run "notepad.exe"] +$wshShell AppActivate $taskId +after 500 +$wshShell SendKeys "The quick brown fox jumped\n" +$wshShell SendKeys "{TAB}over the lazy dog." diff --git a/src/ActiveScriptError.cpp b/src/ActiveScriptError.cpp new file mode 100644 index 0000000..fe219b3 --- /dev/null +++ b/src/ActiveScriptError.cpp @@ -0,0 +1,66 @@ +// $Id: ActiveScriptError.cpp,v 1.1 2002/03/30 18:49:53 cthuang Exp $ +#include "ActiveScriptError.h" + +STDMETHODIMP +ActiveScriptError::QueryInterface (REFIID iid, void **ppvObj) +{ + if (IsEqualIID(iid, IID_IUnknown) + || IsEqualIID(iid, IID_IActiveScriptError)) { + *ppvObj = this; + AddRef(); + return S_OK; + } + + *ppvObj = 0; + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) +ActiveScriptError::AddRef () +{ + InterlockedIncrement(&m_refCount); + return m_refCount; +} + +STDMETHODIMP_(ULONG) +ActiveScriptError::Release () +{ + InterlockedDecrement(&m_refCount); + if (m_refCount == 0) { + delete this; + return 0; + } + return m_refCount; +} + +STDMETHODIMP +ActiveScriptError::GetExceptionInfo (EXCEPINFO *pExcepInfo) +{ + if (pExcepInfo == 0) { + return E_POINTER; + } + + memset(pExcepInfo, 0, sizeof(EXCEPINFO)); + + pExcepInfo->scode = m_hresult; + pExcepInfo->bstrSource = SysAllocString(m_source); + pExcepInfo->bstrDescription = SysAllocString(m_description); + return S_OK; +} + +STDMETHODIMP +ActiveScriptError::GetSourcePosition ( + DWORD *pSourceContext, ULONG *pLineNumber, LONG *pCharacterPosition) +{ + *pSourceContext = 0; + *pLineNumber = m_lineNumber; + *pCharacterPosition = m_characterPosition; + return S_OK; +} + +STDMETHODIMP +ActiveScriptError::GetSourceLineText (BSTR *pSourceLineText) +{ + *pSourceLineText = SysAllocString(m_sourceLineText); + return S_OK; +} diff --git a/src/ActiveScriptError.h b/src/ActiveScriptError.h new file mode 100644 index 0000000..38c542f --- /dev/null +++ b/src/ActiveScriptError.h @@ -0,0 +1,49 @@ +// $Id: ActiveScriptError.h,v 1.2 2002/04/12 02:55:27 cthuang Exp $ +#ifndef ACTIVESCRIPTERROR_H +#define ACTIVESCRIPTERROR_H + +#include +#include + +// This class implements IActiveScriptError. + +class ActiveScriptError: public IActiveScriptError +{ + long m_refCount; + HRESULT m_hresult; + _bstr_t m_source; + _bstr_t m_description; + ULONG m_lineNumber; + long m_characterPosition; + _bstr_t m_sourceLineText; + +public: + ActiveScriptError ( + HRESULT hresult, + const char *source, + const char *description, + ULONG lineNumber, + long characterPosition, + const char *sourceLineText): + m_refCount(0), + m_hresult(hresult), + m_source(source), + m_description(description), + m_lineNumber(lineNumber), + m_characterPosition(characterPosition), + m_sourceLineText(sourceLineText) + { } + + // IUnknown implementation + STDMETHODIMP QueryInterface(REFIID iid, void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // IActiveScriptError implementation + STDMETHODIMP GetExceptionInfo(EXCEPINFO *pExcepInfo); + STDMETHODIMP GetSourcePosition( + DWORD *pSourceContext, ULONG *pLineNumber, LONG *pCharacterPosition); + STDMETHODIMP GetSourceLineText(BSTR *pSourceLineText); +}; + +#endif diff --git a/src/Arguments.cpp b/src/Arguments.cpp new file mode 100644 index 0000000..4ed82a7 --- /dev/null +++ b/src/Arguments.cpp @@ -0,0 +1,308 @@ +// $Id: Arguments.cpp,v 1.33 2002/07/09 04:10:08 cthuang Exp $ +#include "Arguments.h" +#include "Extension.h" +#include "TclObject.h" + +Arguments::Arguments (): + m_args(0) +{ + m_dispParams.rgvarg = NULL; + m_dispParams.rgdispidNamedArgs = NULL; + m_dispParams.cArgs = 0; + m_dispParams.cNamedArgs = 0; +} + +Arguments::~Arguments () +{ + delete[] m_args; +} + + +TypedArguments::TypedArguments (): + m_outValues(0) +{ } + +TypedArguments::~TypedArguments () +{ + delete[] m_outValues; +} + +int +TypedArguments::initArgument ( + Tcl_Interp *interp, + Tcl_Obj *pObj, + int argIndex, + const Parameter ¶meter) +{ + TclObject argument(pObj); + VARTYPE vt = parameter.type().vartype(); + + if (pObj->typePtr == &Extension::naType) { + // This variant indicates a missing optional argument. + m_args[argIndex] = vtMissing; + + } else if (parameter.flags() & PARAMFLAG_FOUT) { + // For out parameters, set a pointer to where the out value + // will be stored. + + if (vt == VT_USERDEFINED) { + // Assume user defined types derive from IUnknown. + vt = VT_UNKNOWN; + } + + if (vt == VT_SAFEARRAY) { + m_args[argIndex].vt = VT_BYREF | VT_ARRAY | + parameter.type().elementType().vartype(); + } else { + m_args[argIndex].vt = VT_BYREF | vt; + } + + if (vt == VT_VARIANT) { + // Set a pointer to out variant. + m_args[argIndex].byref = &m_outValues[argIndex]; + } else { + // Set a pointer to variant data value. + m_args[argIndex].byref = &m_outValues[argIndex].byref; + } + + if (parameter.flags() & PARAMFLAG_FIN) { + // Set the value for an in/out parameter. + Tcl_Obj *pValue = Tcl_ObjGetVar2( + interp, pObj, NULL, TCL_LEAVE_ERR_MSG); + if (pValue == 0) { + return TCL_ERROR; + } + + TclObject value(pValue); + + // If the argument is an interface pointer, increment its reference + // count because the _variant_t destructor will release it. + value.toVariant( + &m_outValues[argIndex], parameter.type(), interp, true); + } else { + if (vt == VT_UNKNOWN) { + m_outValues[argIndex].vt = vt; + m_outValues[argIndex].punkVal = NULL; + } else if (vt == VT_DISPATCH) { + m_outValues[argIndex].vt = vt; + m_outValues[argIndex].pdispVal = NULL; + } else if (vt == VT_SAFEARRAY) { + VARTYPE elementType = parameter.type().elementType().vartype(); + m_outValues[argIndex].vt = VT_ARRAY | elementType; + m_outValues[argIndex].parray = + SafeArrayCreateVector(elementType, 0, 1); + } else if (vt != VT_VARIANT) { + m_outValues[argIndex].ChangeType(vt); + } + } + + } else { + // If the argument is an interface pointer, increment its reference + // count because the _variant_t destructor will release it. + argument.toVariant(&m_args[argIndex], parameter.type(), interp, true); + } + + return TCL_OK; +} + +void +TypedArguments::storeOutValues ( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[], + const Method::Parameters ¶meters) +{ + if (objc > 0) { + int j = objc - 1; + Method::Parameters::const_iterator p = parameters.begin(); + for (int i = 0; i < objc && p != parameters.end(); ++i, --j, ++p) { + if (p->flags() & PARAMFLAG_FOUT) { + TclObject value(&m_outValues[j], p->type(), interp); + Tcl_ObjSetVar2( + interp, objv[i], NULL, value, TCL_LEAVE_ERR_MSG); + } + } + } +} + + +int +PositionalArguments::initialize ( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[], + const Method &method, + WORD dispatchFlags) +{ + const Method::Parameters ¶meters = method.parameters(); + + int paramCount = parameters.size(); + int inputCount = objc; + if (dispatchFlags == DISPATCH_PROPERTYPUT + || dispatchFlags == DISPATCH_PROPERTYPUTREF) { + paramCount = objc; + --inputCount; + } + + if (method.vararg() && inputCount > 0) { + m_args = new _variant_t[inputCount]; + + // Convert the arguments actually provided. + int inputIndex = 0; + int argIndex = inputCount - 1; + for (; inputIndex < inputCount; ++inputIndex, --argIndex) { + TclObject value(objv[inputIndex]); + value.toVariant(&m_args[argIndex], Type::variant(), interp, true); + } + + paramCount = inputCount; + + } else if (paramCount > 0) { + m_args = new _variant_t[paramCount]; + m_outValues = new _variant_t[paramCount]; + + int j = paramCount - 1; + Method::Parameters::const_iterator p = parameters.begin(); + + // Convert the arguments actually provided. + int i = 0; + for (; i < inputCount; ++i, --j, ++p) { + int result = initArgument(interp, objv[i], j, *p); + if (result != TCL_OK) { + return result; + } + } + + // Fill in missing arguments. + for (; p != parameters.end(); ++p, --j) { + m_args[j] = vtMissing; + } + + // Convert argument for property put operations. + if (dispatchFlags == DISPATCH_PROPERTYPUT + || dispatchFlags == DISPATCH_PROPERTYPUTREF) { + TclObject value = objv[i]; + value.toVariant(&m_args[j], method.type(), interp, true); + } + } + + m_dispParams.rgvarg = m_args; + m_dispParams.cArgs = paramCount; + + if (dispatchFlags == DISPATCH_PROPERTYPUT + || dispatchFlags == DISPATCH_PROPERTYPUTREF) { + // Property puts have a named argument that represents the value being + // assigned to the property. + static DISPID mydispid = DISPID_PROPERTYPUT; + m_dispParams.rgdispidNamedArgs = &mydispid; + m_dispParams.cNamedArgs = 1; + } + + return TCL_OK; +} + + +NamedArguments::~NamedArguments () +{ + delete[] m_namedDispids; +} + +Method::Parameters::const_iterator +NamedArguments::findParameter (const Method::Parameters ¶meters, + const std::string &name, + DISPID &dispid) +{ + int i = 0; + Method::Parameters::const_iterator p = parameters.begin(); + for (; p != parameters.end(); ++p, ++i) { + if (p->name() == name) { + dispid = i; + break; + } + } + return p; +} + +int +NamedArguments::initialize ( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[], + const Method &method, + WORD /*dispatchFlags*/) +{ + const Method::Parameters ¶meters = method.parameters(); + + if (objc % 2 != 0) { + Tcl_AppendResult(interp, "name value pairs required", NULL); + return TCL_ERROR; + } + + int cArgs = objc / 2; + if (cArgs > 0) { + m_args = new _variant_t[cArgs]; + m_outValues = new _variant_t[cArgs]; + m_namedDispids = new DISPID[cArgs]; + + int j = cArgs - 1; + for (int i = 0; i < objc; i += 2, --j) { + char *name = Tcl_GetStringFromObj(objv[i], 0); + Method::Parameters::const_iterator p = findParameter( + parameters, + name, + m_namedDispids[j]); + if (p == parameters.end()) { + Tcl_AppendResult(interp, "unknown parameter ", name, NULL); + return TCL_ERROR; + } + + int result = initArgument(interp, objv[i+1], j, *p); + if (result != TCL_OK) { + return result; + } + } + } + + m_dispParams.rgvarg = m_args; + m_dispParams.rgdispidNamedArgs = m_namedDispids; + m_dispParams.cArgs = cArgs; + m_dispParams.cNamedArgs = cArgs; + + return TCL_OK; +} + + +int +UntypedArguments::initialize ( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[], + WORD dispatchFlags) +{ + if (objc > 0) { + m_args = new _variant_t[objc]; + + int j = objc - 1; + for (int i = 0; i < objc; ++i, --j) { + TclObject value(objv[i]); + + // If the argument is an interface pointer, increment its reference + // count because the _variant_t destructor will release it. + value.toVariant(&m_args[j], Type::variant(), interp, true); + } + } + + m_dispParams.rgvarg = m_args; + m_dispParams.cArgs = objc; + + if (dispatchFlags == DISPATCH_PROPERTYPUT + || dispatchFlags == DISPATCH_PROPERTYPUTREF) { + // Property puts have a named argument that represents the value being + // assigned to the property. + static DISPID mydispid = DISPID_PROPERTYPUT; + m_dispParams.rgdispidNamedArgs = &mydispid; + m_dispParams.cNamedArgs = 1; + } + + return TCL_OK; +} diff --git a/src/Arguments.h b/src/Arguments.h new file mode 100644 index 0000000..50e57f9 --- /dev/null +++ b/src/Arguments.h @@ -0,0 +1,124 @@ +// $Id: Arguments.h,v 1.8 2001/10/13 17:56:14 Administrator Exp $ +#ifndef ARGUMENTS_H +#define ARGUMENTS_H + +#include "TypeInfo.h" + +class Arguments +{ +protected: + DISPPARAMS m_dispParams; + + // argument values + _variant_t *m_args; + + Arguments(); + +public: + virtual ~Arguments(); + + // Get arguments in the format required by the Invoke function. + DISPPARAMS *dispParams () const + { return const_cast(&m_dispParams); } +}; + +// This abstract class represents the arguments passed in and out of a method +// for the case where we know the types of the parameters. + +class TypedArguments: public Arguments +{ +protected: + // used to hold values returned from out parameters + _variant_t *m_outValues; + + TypedArguments(); + + // Initialize a single argument. + int initArgument( + Tcl_Interp *interp, + Tcl_Obj *obj, + int argIndex, + const Parameter ¶meter); + +public: + virtual ~TypedArguments(); + + // Ready arguments for method invocation. + // Returns a Tcl completion code. + virtual int initialize( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[], + const Method &method, + WORD dispatchFlags) = 0; + + // Put the values returned from out parameters into Tcl variables. + void storeOutValues( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[], + const Method::Parameters ¶meters); +}; + +// This class represents arguments specified by their position in an argument +// list. + +class PositionalArguments: public TypedArguments +{ +public: + // Ready arguments for method invocation. + // Returns a Tcl completion code. + virtual int initialize( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[], + const Method &method, + WORD dispatchFlags); +}; + +// This class represents arguments specified by name. + +class NamedArguments: public TypedArguments +{ + // Search the parameter list for the named parameter. + static Method::Parameters::const_iterator findParameter( + const Method::Parameters ¶meters, + const std::string &name, + DISPID &dispid); + + // IDs of named arguments + DISPID *m_namedDispids; + +public: + NamedArguments (): + m_namedDispids(0) + { } + + ~NamedArguments(); + + // Ready arguments for method invocation. + // Returns a Tcl completion code. + virtual int initialize( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[], + const Method &method, + WORD dispatchFlags); +}; + +// This class represents the arguments passed into a method +// for the case where we don't know the types of the parameters. + +class UntypedArguments: public Arguments +{ +public: + // Ready arguments for method invocation. + // Returns a Tcl result code. + int initialize( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[], + WORD dispatchFlags); +}; + +#endif diff --git a/src/ComModule.cpp b/src/ComModule.cpp new file mode 100644 index 0000000..9ff5224 --- /dev/null +++ b/src/ComModule.cpp @@ -0,0 +1,108 @@ +// $Id: ComModule.cpp,v 1.15 2002/05/31 04:03:06 cthuang Exp $ +#pragma warning(disable: 4786) +#include "ComObjectFactory.h" +#include "ComModule.h" + +// This is the default module for event sink objects. + +class DefaultModule: public ComModule +{ +public: + DefaultModule () + { } + + ~DefaultModule () + { revokeFactories(); } +}; + + +ComModule *ComModule::ms_pInstance; + +Mutex ComModule::ms_singletonMutex; + +ComModule & +ComModule::instance () +{ + if (ms_pInstance == 0) { + LOCK_MUTEX(ms_singletonMutex) + static DefaultModule module; + } + return *ms_pInstance; +} + +// This exit handler uninitializes COM. + +static void +exitProc (ClientData) +{ + CoUninitialize(); +} + +void +ComModule::initializeCom (DWORD coinitFlags) +{ +#ifdef _WIN32_DCOM + CoInitializeEx(NULL, coinitFlags); +#else + CoInitialize(NULL); +#endif + +#ifdef TCL_THREADS + Tcl_CreateThreadExitHandler(exitProc, 0); +#else + Tcl_CreateExitHandler(exitProc, 0); +#endif +} + +DWORD +ComModule::regclsFlags () const +{ + return REGCLS_MULTIPLEUSE; +} + +void +ComModule::lock () +{ + InterlockedIncrement(&m_lockCount); +} + +long +ComModule::unlock () +{ + InterlockedDecrement(&m_lockCount); + return m_lockCount; +} + +void +ComModule::registerFactory (REFCLSID clsid, + ComObjectFactory *pFactory) +{ + pFactory->registerFactory(clsid, regclsFlags()); + + Uuid classId(clsid); + m_clsidToFactoryMap.insert(ClsidToFactoryMap::value_type( + classId, pFactory)); + pFactory->AddRef(); +} + +IClassFactory * +ComModule::find (REFCLSID clsid) +{ + Uuid classId(clsid); + ClsidToFactoryMap::iterator p = m_clsidToFactoryMap.find(classId); + if (p != m_clsidToFactoryMap.end()) { + return p->second; + } + return 0; +} + +void +ComModule::revokeFactories () +{ + ClsidToFactoryMap::iterator p = m_clsidToFactoryMap.begin(); + for (; p != m_clsidToFactoryMap.end(); ++p) { + p->second->Release(); + } + + m_clsidToFactoryMap.clear(); +} diff --git a/src/ComModule.h b/src/ComModule.h new file mode 100644 index 0000000..21816f4 --- /dev/null +++ b/src/ComModule.h @@ -0,0 +1,69 @@ +// $Id: ComModule.h,v 1.13 2002/04/13 03:53:56 cthuang Exp $ +#ifndef COMMODULE_H +#define COMMODULE_H + +#include +#include +#include "tcomApi.h" +#include "Uuid.h" +#include "mutex.h" + +class ComObjectFactory; + +// This class manages the life cycle of a COM server. + +class TCOM_API ComModule +{ + // used to track when the server can exit + long m_lockCount; + + // This maps a CLSID to a class factory. + typedef std::map ClsidToFactoryMap; + ClsidToFactoryMap m_clsidToFactoryMap; + + // singleton instance + static ComModule *ms_pInstance; + + // used to synchonize construction of singleton instance + static Mutex ms_singletonMutex; + + // Do not allow others to create and copy instances of this class. + ComModule(const ComModule &); // not implemented + void operator=(const ComModule &); // not implemented + +protected: + ComModule (): + m_lockCount(0) + { ms_pInstance = this; } + + // Get class object registration flags. + virtual DWORD regclsFlags() const; + +public: + // Get singleton instance. + static ComModule &instance(); + + // Initialize COM for the current thread. + virtual void initializeCom(DWORD coinitFlags); + + // Get lock count. + long lockCount () const + { return m_lockCount; } + + // Increment lock count. + virtual void lock(); + + // Decrement lock count. + virtual long unlock(); + + // Register a class factory. + void registerFactory(REFCLSID clsid, ComObjectFactory *pFactory); + + // Search for a class factory by CLSID. + IClassFactory *find(REFCLSID clsid); + + // Revoke all class factories. + void revokeFactories(); +}; + +#endif diff --git a/src/ComObject.cpp b/src/ComObject.cpp new file mode 100644 index 0000000..e0fc187 --- /dev/null +++ b/src/ComObject.cpp @@ -0,0 +1,844 @@ +// $Id: ComObject.cpp,v 1.37 2002/05/31 04:03:06 cthuang Exp $ +#pragma warning(disable: 4786) +#include "ComObject.h" +#include +#include "ComModule.h" +#include "InterfaceAdapter.h" +#include "Reference.h" +#include "Extension.h" + +// prefix prepended to operation name for property get +static const char getPrefix[] = "_get_"; + +// prefix prepended to operation name for property put +static const char setPrefix[] = "_set_"; + +ComObject::ComObject (const Class::Interfaces &interfaces, + Tcl_Interp *interp, + TclObject servant, + TclObject destructor): + m_refCount(0), + m_defaultInterface(*(interfaces.front())), + m_interp(interp), + m_servant(servant), + m_destructor(destructor), + m_supportErrorInfo(*this), + m_pDispatch(0) +{ +// Tcl_Preserve(reinterpret_cast(m_interp)); + ComModule::instance().lock(); + + for (Class::Interfaces::const_iterator p = interfaces.begin(); + p != interfaces.end(); ++p) { + const Interface *pInterface = *p; + m_supportedInterfaceMap.insert( + pInterface->iid(), const_cast(pInterface)); + } + + m_pDefaultAdapter = implementInterface(m_defaultInterface); +} + +ComObject::~ComObject () +{ + if (m_registeredActiveObject) { + // TODO: This call may return an error but I don't want to throw an + // exception from a destructor. + RevokeActiveObject(m_activeObjectHandle, 0); + } + + m_iidToAdapterMap.forEach(Delete()); + delete m_pDispatch; + + // Execute destructor Tcl command if defined. + int length; + Tcl_GetStringFromObj(m_destructor, &length); + if (length > 0) { + TclObject script(m_destructor); + script.lappend(m_servant); + eval(script); + } + + ComModule::instance().unlock(); +// Tcl_Release(reinterpret_cast(m_interp)); +} + +void +ComObject::registerActiveObject (REFCLSID clsid) +{ + HRESULT hr = RegisterActiveObject( + unknown(), clsid, ACTIVEOBJECT_WEAK, &m_activeObjectHandle); + if (FAILED(hr)) { + _com_issue_error(hr); + } + m_registeredActiveObject = true; +} + +InterfaceAdapter * +ComObject::implementInterface (const Interface &interfaceDesc) +{ + InterfaceAdapter *pAdapter = new InterfaceAdapter(*this, interfaceDesc); + m_iidToAdapterMap.insert(interfaceDesc.iid(), pAdapter); + return pAdapter; +} + +ComObject * +ComObject::newInstance ( + const Interface &defaultInterface, + Tcl_Interp *interp, + TclObject servant, + TclObject destructor) +{ + Class::Interfaces interfaces; + interfaces.push_back(&defaultInterface); + + return new ComObject( + interfaces, + interp, + servant, + destructor); +} + +ComObject * +ComObject::newInstance ( + const Class::Interfaces &interfaces, + Tcl_Interp *interp, + TclObject servant, + TclObject destructor) +{ + ComObject *pComObject = new ComObject( + interfaces, + interp, + servant, + destructor); + return pComObject; +} + +int +ComObject::eval (TclObject script, TclObject *pResult) +{ + int completionCode = +#if TCL_MINOR_VERSION >= 1 + Tcl_EvalObjEx(m_interp, script, TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL); +#else + Tcl_GlobalEvalObj(m_interp, script); +#endif + + if (pResult != 0) { + *pResult = Tcl_GetObjResult(m_interp); + } + return completionCode; +} + +int +ComObject::getVariable (TclObject name, TclObject &value) const +{ + Tcl_Obj *pValue = Tcl_ObjGetVar2(m_interp, name, 0, TCL_LEAVE_ERR_MSG); + if (pValue == 0) { + return TCL_ERROR; + } + value = pValue; + return TCL_OK; +} + +int +ComObject::setVariable (TclObject name, TclObject value) +{ + Tcl_Obj *pValue = + Tcl_ObjSetVar2(m_interp, name, 0, value, TCL_LEAVE_ERR_MSG); + return (pValue == 0) ? TCL_ERROR : TCL_OK; +} + +HRESULT +ComObject::hresultFromErrorCode () const +{ +#if TCL_MINOR_VERSION >= 1 + Tcl_Obj *pErrorCode = + Tcl_GetVar2Ex(m_interp, "::errorCode", 0, TCL_LEAVE_ERR_MSG); +#else + TclObject errorCodeVarName("::errorCode"); + Tcl_Obj *pErrorCode = + Tcl_ObjGetVar2(m_interp, errorCodeVarName, 0, TCL_LEAVE_ERR_MSG); +#endif + + if (pErrorCode == 0) { + return E_UNEXPECTED; + } + + Tcl_Obj *pErrorClass; + if (Tcl_ListObjIndex(m_interp, pErrorCode, 0, &pErrorClass) != TCL_OK) { + return E_UNEXPECTED; + } + if (strcmp(Tcl_GetStringFromObj(pErrorClass, 0), "COM") != 0) { + return E_UNEXPECTED; + } + + Tcl_Obj *pHresult; + if (Tcl_ListObjIndex(m_interp, pErrorCode, 1, &pHresult) != TCL_OK) { + return E_UNEXPECTED; + } + + HRESULT hr; + if (Tcl_GetLongFromObj(m_interp, pHresult, &hr) != TCL_OK) { + return E_UNEXPECTED; + } + return hr; +} + +// Implement IUnknown methods + +HRESULT +ComObject::queryInterface (REFIID iid, void **ppvObj) +{ + if (IsEqualIID(iid, IID_IUnknown)) { + *ppvObj = m_pDefaultAdapter; + addRef(); + return S_OK; + } + + if (IsEqualIID(iid, IID_IDispatch)) { + // Expose the operations of the default interface through IDispatch. + if (m_pDispatch == 0) { + m_pDispatch = new InterfaceAdapter(*this, m_defaultInterface, true); + } + *ppvObj = m_pDispatch; + addRef(); + return S_OK; + } + + if (IsEqualIID(iid, IID_ISupportErrorInfo)) { + *ppvObj = &m_supportErrorInfo; + addRef(); + return S_OK; + } + + InterfaceAdapter *pAdapter = m_iidToAdapterMap.find(iid); + if (pAdapter == 0) { + const Interface *pInterface = m_supportedInterfaceMap.find(iid); + if (pInterface != 0) { + pAdapter = implementInterface(*pInterface); + } + } + + if (pAdapter != 0) { + *ppvObj = pAdapter; + addRef(); + return S_OK; + } + + *ppvObj = 0; + return E_NOINTERFACE; +} + +ULONG +ComObject::addRef () +{ + InterlockedIncrement(&m_refCount); + return m_refCount; +} + +ULONG +ComObject::release () +{ + InterlockedDecrement(&m_refCount); + if (m_refCount == 0) { + delete this; + return 0; + } + return m_refCount; +} + +// Generate a name for a Tcl variable used to hold an argument out value. + +static TclObject +getOutVariableName (const Parameter ¶m) +{ + return TclObject(PACKAGE_NAMESPACE "arg_" + param.name()); +} + +// Convert IDispatch argument to Tcl value. + +TclObject +ComObject::getArgument (VARIANT *pArg, const Parameter ¶m) +{ + if (vtMissing == pArg) { + return Extension::newNaObj(); + + } else if (param.flags() & PARAMFLAG_FOUT) { + // Get name of Tcl variable to hold out value. + TclObject varName = getOutVariableName(param); + + if (param.flags() & PARAMFLAG_FIN) { + // For in/out parameters, set the Tcl variable to the input value. + TclObject value(pArg, param.type(), m_interp); + setVariable(varName, value); + } + return varName; + + } else { + return TclObject(pArg, param.type(), m_interp); + } +} + +// Fill exception information structure. + +static void +fillExcepInfo (EXCEPINFO *pExcepInfo, + HRESULT hresult, + const char *source, + const char *description) +{ + if (pExcepInfo != 0) { + memset(pExcepInfo, 0, sizeof(EXCEPINFO)); + pExcepInfo->scode = hresult; + + _bstr_t bstrSource(source); + pExcepInfo->bstrSource = SysAllocString(bstrSource); + + if (description != 0) { + _bstr_t bstrDescription(description); + pExcepInfo->bstrDescription = SysAllocString(bstrDescription); + } + } +} + +static void +putOutVariant (Tcl_Interp *interp, + VARIANT *pDest, + TclObject &tclObject, + const Type &type) +{ + switch (type.vartype()) { + case VT_BOOL: + *V_BOOLREF(pDest) = tclObject.getBool() ? VARIANT_TRUE : VARIANT_FALSE; + break; + + case VT_R4: + *V_R4REF(pDest) = static_cast(tclObject.getDouble()); + break; + + case VT_R8: + *V_R8REF(pDest) = tclObject.getDouble(); + break; + + case VT_DISPATCH: + case VT_UNKNOWN: + case VT_USERDEFINED: + { + IUnknown *pUnknown; + + Tcl_Obj *pObj = tclObject; + if (pObj->typePtr == &Extension::unknownPointerType) { + pUnknown = + static_cast(pObj->internalRep.otherValuePtr); + } else { + Reference *pRef = Extension::referenceHandles.find( + interp, tclObject); + pUnknown = (pRef == 0) ? 0 : pRef->unknown(); + } + + *V_UNKNOWNREF(pDest) = pUnknown; + + // The COM rules say we must increment the reference count of + // interface pointers returned from methods. + if (pUnknown != 0) { + pUnknown->AddRef(); + } + } + break; + + case VT_BSTR: + *V_BSTRREF(pDest) = tclObject.getBSTR(); + break; + + case VT_VARIANT: + { + // Must increment reference count of interface pointers returned + // from methods. + tclObject.toVariant( + V_VARIANTREF(pDest), Type::variant(), interp, true); + } + break; + + default: + *V_I4REF(pDest) = tclObject.getLong(); + } +} + +HRESULT +ComObject::invoke (InterfaceAdapter *pAdapter, + DISPID dispid, + REFIID /*riid*/, + LCID /*lcid*/, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pReturnValue, + EXCEPINFO *pExcepInfo, + UINT *pArgErr) +{ + // Get the method description for method being invoked. + const Method *pMethod = pAdapter->findDispatchMethod(dispid); + if (pMethod == 0) { + return DISP_E_MEMBERNOTFOUND; + } + + HRESULT hresult; + + try { + // Construct Tcl script to invoke operation on the servant. + TclObject script(m_servant); + + // Get the method or property to invoke on the servant. + std::string operation; + if ((wFlags & DISPATCH_PROPERTYGET) != 0 + && pAdapter->isProperty(dispid)) { + operation = getPrefix + pMethod->name(); + + } else if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) { + operation = setPrefix + pMethod->name(); + + } else if (wFlags & DISPATCH_METHOD) { + operation = pMethod->name(); + + } else { + return DISP_E_MEMBERNOTFOUND; + } + + script.lappend( + Tcl_NewStringObj(const_cast(operation.c_str()), -1)); + + // Set the argument error pointer in case we need to use it. + UINT argErr; + if (pArgErr == 0) { + pArgErr = &argErr; + } + + // Convert arguments to Tcl values. + // TODO: Should handle named arguments differently than positional + // arguments. + const Method::Parameters ¶meters = pMethod->parameters(); + + int argIndex = pDispParams->cArgs - 1; + Method::Parameters::const_iterator pParam; + for (pParam = parameters.begin(); pParam != parameters.end(); + ++pParam, --argIndex) { + // Append argument value. + VARIANT *pArg = &(pDispParams->rgvarg[argIndex]); + try { + script.lappend(getArgument(pArg, *pParam)); + } + catch (_com_error &) { + *pArgErr = argIndex; + throw; + } + } + + if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) { + VARIANT *pArg = &(pDispParams->rgvarg[argIndex]); + try { + TclObject value(pArg, pMethod->type(), m_interp); + script.lappend(value); + } + catch (_com_error &) { + *pArgErr = argIndex; + throw; + } + } + + // Execute the Tcl script. + TclObject result; + int completionCode = eval(script, &result); + if (completionCode != TCL_OK) { + fillExcepInfo( + pExcepInfo, + hresultFromErrorCode(), + m_servant.c_str(), + result.c_str()); + return DISP_E_EXCEPTION; + } + + // Copy values to out arguments. + argIndex = pDispParams->cArgs - 1; + for (pParam = parameters.begin(); pParam != parameters.end(); + ++pParam, --argIndex) { + if (pParam->flags() & PARAMFLAG_FOUT) { + // Get name of Tcl variable that holds out value. + TclObject varName = getOutVariableName(*pParam); + + // Copy variable value to out argument. + TclObject value; + if (getVariable(varName, value) == TCL_OK) { + putOutVariant( + m_interp, + &pDispParams->rgvarg[argIndex], + value, + pParam->type()); + } + } + } + + // Convert return value. + if (pReturnValue != 0 && pMethod->type().vartype() != VT_VOID) { + // Must increment reference count of interface pointers returned + // from methods. + result.toVariant(pReturnValue, pMethod->type(), m_interp, true); + } + + hresult = S_OK; + } + catch (_com_error &e) { + fillExcepInfo(pExcepInfo, e.Error(), m_servant.c_str(), 0); + hresult = DISP_E_EXCEPTION; + } + return hresult; +} + +// Convert the native value that the va_list points to into a Tcl object. +// Returns a va_list pointing to the next argument. + +static va_list +convertNativeToTclObject (va_list pArg, + Tcl_Interp *interp, + TclObject &tclObject, + const Type &type, + bool byRef=false) +{ + switch (type.vartype()) { + case VT_BOOL: + tclObject = Tcl_NewBooleanObj( + byRef ? *va_arg(pArg, VARIANT_BOOL *) : va_arg(pArg, VARIANT_BOOL)); + break; + + case VT_DATE: + case VT_R4: + case VT_R8: + tclObject = Tcl_NewDoubleObj( + byRef ? *va_arg(pArg, double *) : va_arg(pArg, double)); + break; + + case VT_USERDEFINED: + if (type.name() == "GUID") { + UUID *pUuid = va_arg(pArg, UUID *); + Uuid uuid(*pUuid); + tclObject = Tcl_NewStringObj( + const_cast(uuid.toString().c_str()), -1); + break; + } + // Fall through + + case VT_DISPATCH: + case VT_UNKNOWN: + { + IUnknown *pUnknown = va_arg(pArg, IUnknown *); + if (pUnknown == 0) { + tclObject = Tcl_NewObj(); + } else { + const Interface *pInterface = + InterfaceManager::instance().find(type.iid()); + tclObject = Extension::referenceHandles.newObj( + interp, Reference::newReference(pUnknown, pInterface)); + } + } + break; + + case VT_NULL: + tclObject = Tcl_NewObj(); + break; + + case VT_LPWSTR: + case VT_BSTR: + { +#if TCL_MINOR_VERSION >= 2 + // Uses Unicode function introduced in Tcl 8.2. + Tcl_UniChar *pUnicode = va_arg(pArg, Tcl_UniChar *); + if (pUnicode != 0) { + tclObject = Tcl_NewUnicodeObj(pUnicode, -1); + } else { + tclObject = Tcl_NewObj(); + } +#else + _bstr_t str(va_arg(pArg, wchar_t *)); + tclObject = Tcl_NewStringObj(str, -1); +#endif + } + break; + + default: + tclObject = Tcl_NewLongObj( + byRef ? *va_arg(pArg, int *) : va_arg(pArg, int)); + } + + return pArg; +} + +// Convert the native value that the va_list points to into a Tcl value. +// Returns a va_list pointing to the next argument. + +va_list +ComObject::getArgument ( + va_list pArg, const Parameter ¶m, TclObject &dest) +{ + if (param.flags() & PARAMFLAG_FOUT) { + // Get name of Tcl variable to hold out value. + TclObject varName = getOutVariableName(param); + + if (param.flags() & PARAMFLAG_FIN) { + // For in/out parameters, set the Tcl variable to the input value. + TclObject value; + pArg = convertNativeToTclObject( + pArg, m_interp, value, param.type(), true); + setVariable(varName, value); + } else { + // Advance to next argument. + va_arg(pArg, void *); + } + dest = varName; + return pArg; + + } else { + return convertNativeToTclObject( + pArg, m_interp, dest, param.type()); + } +} + +// Convert Tcl value to native value and store it at the address the va_list +// points to. +// Returns a va_list pointing to the next argument. + +static va_list +putArgument (va_list pArg, + Tcl_Interp *interp, + TclObject tclObject, + const Type &type) +{ + void *pDest = va_arg(pArg, void *); + if (pDest == 0) { + return pArg; + } + + switch (type.vartype()) { + case VT_BOOL: + *static_cast(pDest) = + tclObject.getBool() ? VARIANT_TRUE : VARIANT_FALSE; + break; + + case VT_R4: + *static_cast(pDest) = + static_cast(tclObject.getDouble()); + break; + + case VT_R8: + *static_cast(pDest) = tclObject.getDouble(); + break; + + case VT_USERDEFINED: + if (type.name() == "GUID") { + char *uuidStr = const_cast(tclObject.c_str()); + UUID uuid; + UuidFromString(reinterpret_cast(uuidStr), &uuid); + *static_cast(pDest) = uuid; + break; + } + // Fall through + + case VT_DISPATCH: + case VT_UNKNOWN: + { + IUnknown *pUnknown; + + Tcl_Obj *pObj = tclObject; + if (pObj->typePtr == &Extension::unknownPointerType) { + pUnknown = + static_cast(pObj->internalRep.otherValuePtr); + } else { + Reference *pRef = Extension::referenceHandles.find( + interp, tclObject); + pUnknown = (pRef == 0) ? 0 : pRef->unknown(); + } + + *static_cast(pDest) = pUnknown; + + // The COM rules say we must increment the reference count of + // interface pointers returned from methods. + if (pUnknown != 0) { + pUnknown->AddRef(); + } + } + break; + + case VT_BSTR: + *static_cast(pDest) = tclObject.getBSTR(); + break; + + case VT_VARIANT: + { + // Must increment reference count of interface pointers returned + // from methods. + tclObject.toVariant( + static_cast(pDest), + Type::variant(), + interp, + true); + } + break; + + default: + *static_cast(pDest) = tclObject.getLong(); + } + + return pArg; +} + +// Advance the va_list to the next argument. +// Returns a va_list pointing to the next argument. + +static va_list +nextArgument (va_list pArg, const Type &type) +{ + switch (type.vartype()) { + case VT_R4: + case VT_R8: + case VT_DATE: + va_arg(pArg, double); + break; + + case VT_DISPATCH: + case VT_UNKNOWN: + case VT_USERDEFINED: + va_arg(pArg, IUnknown *); + break; + + case VT_BSTR: + va_arg(pArg, BSTR); + break; + + default: + va_arg(pArg, int); + } + + return pArg; +} + +// Set error info. + +static void +setErrorInfo (const char *source, const char *description) +{ + HRESULT hr; + + ICreateErrorInfoPtr pCreateErrorInfo; + hr = CreateErrorInfo(&pCreateErrorInfo); + if (FAILED(hr)) { + return; + } + + _bstr_t sourceBstr(source); + pCreateErrorInfo->SetSource(sourceBstr); + + _bstr_t descriptionBstr(description); + pCreateErrorInfo->SetDescription(descriptionBstr); + + IErrorInfoPtr pErrorInfo; + hr = pCreateErrorInfo->QueryInterface( + IID_IErrorInfo, reinterpret_cast(&pErrorInfo)); + if (SUCCEEDED(hr)) { + SetErrorInfo(0, pErrorInfo); + } +} + +// Note that this function is called in an odd way to avoid copying the +// arguments onto the stack (for efficiency and simplicity in the calling +// code). This is why the call is explicitly declared __cdecl. + +void __cdecl +invokeComObjectFunction (volatile HRESULT hresult, + volatile DWORD pArgEnd, + DWORD /*ebp*/, + DWORD funcIndex, + DWORD /*retAddr*/, + InterfaceAdapter *pAdapter, + ...) +{ + // Get the method description for method being invoked. + const Method *pMethod = pAdapter->findComMethod(funcIndex); + if (pMethod == 0) { + // If we don't have a method description, we don't know how many bytes + // the arguments take on the stack. + throw std::runtime_error("unknown virtual function index"); + } + + ComObject &object = pAdapter->object(); + + // Construct Tcl script to invoke operation on the servant. + TclObject script(object.m_servant); + + std::string operation; + switch (pMethod->invokeKind()) { + case INVOKE_PROPERTYGET: + operation = getPrefix + pMethod->name(); + break; + case INVOKE_PROPERTYPUT: + case INVOKE_PROPERTYPUTREF: + operation = setPrefix + pMethod->name(); + break; + default: + operation = pMethod->name(); + } + script.lappend( + Tcl_NewStringObj(const_cast(operation.c_str()), -1)); + + // Convert arguments to Tcl values. + va_list pArg; + va_start(pArg, pAdapter); + const Method::Parameters ¶meters = pMethod->parameters(); + Method::Parameters::const_iterator pParam = parameters.begin(); + for (; pParam != parameters.end(); ++pParam) { + // Append argument value. + TclObject argument; + pArg = object.getArgument(pArg, *pParam, argument); + script.lappend(argument); + } + + // Set end of arguments pointer. + if (pMethod->type().vartype() != VT_VOID) { + va_arg(pArg, void *); + } + pArgEnd = reinterpret_cast(pArg); + va_end(pArg); + + // Execute the Tcl script. + TclObject result; + int completionCode = object.eval(script, &result); + if (completionCode == TCL_OK) { + hresult = S_OK; + } else { + hresult = object.hresultFromErrorCode(); + setErrorInfo(object.m_servant.c_str(), result.c_str()); + } + + // Copy values to out arguments. + va_start(pArg, pAdapter); + pParam = parameters.begin(); + for (; pParam != parameters.end(); ++pParam) { + if (pParam->flags() & PARAMFLAG_FOUT) { + // Get name of Tcl variable that holds out value. + TclObject varName = getOutVariableName(*pParam); + + // Copy variable value to out argument. + TclObject value; + if (object.getVariable(varName, value) == TCL_OK) { + pArg = putArgument( + pArg, object.m_interp, value, pParam->type()); + continue; + } + } + + pArg = nextArgument(pArg, pParam->type()); + } + + // Convert return value. + if (pMethod->type().vartype() != VT_VOID) { + putArgument(pArg, object.m_interp, result, pMethod->type()); + } + + va_end(pArg); +} diff --git a/src/ComObject.h b/src/ComObject.h new file mode 100644 index 0000000..1d71c0a --- /dev/null +++ b/src/ComObject.h @@ -0,0 +1,145 @@ +// $Id: ComObject.h,v 1.14 2002/04/13 03:53:56 cthuang Exp $ +#ifndef COMOBJECT_H +#define COMOBJECT_H + +#include +#include "tcomApi.h" +#include "HashTable.h" +#include "TclObject.h" +#include "TypeInfo.h" +#include "SupportErrorInfo.h" + +class TCOM_API InterfaceAdapter; + +// This class represents a COM object. +// The COM object methods are implemented by executing a Tcl command. + +class TCOM_API ComObject +{ + // Implement method invocation through virtual function table call. + friend void __cdecl invokeComObjectFunction( + volatile HRESULT hresult, + volatile DWORD pArgEnd, + DWORD ebp, + DWORD funcIndex, + DWORD retAddr, + InterfaceAdapter *pThis, + ...); + + // count of references to this object + long m_refCount; + + // description of default interface + const Interface &m_defaultInterface; + + // TODO: Directly accessing the Tcl interpreter means the object must run + // in a single threaded apartment to comply with Tcl's threading rules. + + // interpreter used to execute Tcl command + Tcl_Interp *m_interp; + + // Tcl command executed to implement methods + TclObject m_servant; + + // Tcl command executed when this COM object is destroyed + TclObject m_destructor; + + // collection of interfaces this object can implement + typedef HashTable SupportedInterfaceMap; + SupportedInterfaceMap m_supportedInterfaceMap; + + // collection of implemented interface adapters + typedef HashTable IidToAdapterMap; + IidToAdapterMap m_iidToAdapterMap; + + // implements default interface + InterfaceAdapter *m_pDefaultAdapter; + + // implements ISupportErrorInfo + SupportErrorInfo m_supportErrorInfo; + + // implements IDispatch + InterfaceAdapter *m_pDispatch; + + // token returned from RegisterActiveObject + unsigned long m_activeObjectHandle; + + // true if object registered in running object table + bool m_registeredActiveObject; + + // Do not allow others to create or copy instances of this class. + ComObject( + const Class::Interfaces &interfaces, + Tcl_Interp *interp, + TclObject servant, + TclObject destructor); + ComObject(const ComObject &); // not implemented + void operator=(const ComObject &); // not implemented + + // Create an adapter which implements the specified interface. + InterfaceAdapter *implementInterface(const Interface &interfaceDesc); + + // Convert IDispatch argument to Tcl value. + TclObject getArgument(VARIANT *pArg, const Parameter ¶m); + + // Convert the native value that the va_list points to into a Tcl value. + // Returns a va_list pointing to the next argument. + va_list getArgument(va_list pArg, const Parameter ¶m, TclObject &dest); + +public: + static ComObject *newInstance( + const Interface &defaultInterface, + Tcl_Interp *interp, + TclObject servant, + TclObject destructor); + static ComObject *newInstance( + const Class::Interfaces &interfaces, + Tcl_Interp *interp, + TclObject servant, + TclObject destructor); + ~ComObject(); + + // Register object in running object table. + void registerActiveObject(REFCLSID clsid); + + // Return true if the interface is implemented. + bool implemented (REFIID iid) const + { return m_iidToAdapterMap.find(iid) != 0; } + + // Get IUnknown pointer to default interface. + IUnknown *unknown () const + { return reinterpret_cast(m_pDefaultAdapter); } + + // Execute Tcl script. Returns Tcl completion code. + int eval(TclObject script, TclObject *pResult=0); + + // Get Tcl variable. Returns Tcl completion code. + int getVariable(TclObject name, TclObject &value) const; + + // Set Tcl variable. Returns Tcl completion code. + int setVariable(TclObject name, TclObject value); + + // If the first element of the Tcl errorCode variable is "COM", convert + // second element to an HRESULT. Return E_UNEXPECTED if errorCode does + // not contain a recognizable value. + HRESULT hresultFromErrorCode() const; + + // IUnknown implementation + HRESULT queryInterface(REFIID riid, void **ppvObj); + ULONG addRef(); + ULONG release(); + + // IDispatch implementation + HRESULT invoke( + InterfaceAdapter *pThis, + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pdispparams, + VARIANT *pvarResult, + EXCEPINFO *pexcepinfo, + UINT *puArgErr); +}; + +#endif diff --git a/src/ComObjectFactory.cpp b/src/ComObjectFactory.cpp new file mode 100644 index 0000000..8176be5 --- /dev/null +++ b/src/ComObjectFactory.cpp @@ -0,0 +1,179 @@ +// $Id: ComObjectFactory.cpp,v 1.17 2002/05/31 04:03:06 cthuang Exp $ +#pragma warning(disable: 4786) +#include "ComModule.h" +#include "ComObject.h" +#include "ComObjectFactory.h" + +ComObjectFactory::ComObjectFactory (const Class::Interfaces &interfaces, + Tcl_Interp *interp, + TclObject constructor, + TclObject destructor, + bool registerActiveObject): + m_refCount(0), + m_interfaces(interfaces), + m_interp(interp), + m_constructor(constructor), + m_destructor(destructor), + m_registerActiveObject(registerActiveObject), + m_registeredFactory(false) +{ } + +ComObjectFactory::~ComObjectFactory () +{ + if (m_registeredFactory) { + // TODO: This call can return an error but I don't want to throw an + // exception from a destructor. + CoRevokeClassObject(m_classObjectHandle); + } +} + +void +ComObjectFactory::registerFactory (REFCLSID clsid, DWORD regclsFlags) +{ + m_clsid = clsid; + + HRESULT hr = CoRegisterClassObject( + clsid, + this, + CLSCTX_SERVER, + regclsFlags, + &m_classObjectHandle); + if (FAILED(hr)) { + _com_issue_error(hr); + } + m_registeredFactory = true; +} + +STDMETHODIMP +ComObjectFactory::QueryInterface (REFIID iid, void **ppvObj) +{ + if (IsEqualIID(iid, IID_IClassFactory) || IsEqualIID(iid, IID_IUnknown)) { + *ppvObj = this; + AddRef(); + return S_OK; + } + + *ppvObj = 0; + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) +ComObjectFactory::AddRef () +{ + InterlockedIncrement(&m_refCount); + return m_refCount; +} + +STDMETHODIMP_(ULONG) +ComObjectFactory::Release () +{ + InterlockedDecrement(&m_refCount); + if (m_refCount == 0) { + delete this; + return 0; + } + return m_refCount; +} + +int +ComObjectFactory::eval (TclObject script, TclObject *pResult) +{ + int completionCode = +#if TCL_MINOR_VERSION >= 1 + Tcl_EvalObjEx(m_interp, script, TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL); +#else + Tcl_GlobalEvalObj(m_interp, script); +#endif + + if (pResult != 0) { + *pResult = Tcl_GetObjResult(m_interp); + } + return completionCode; +} + +STDMETHODIMP +ComObjectFactory::CreateInstance (IUnknown *pOuter, REFIID iid, void **ppvObj) +{ + // We don't support aggregation. + if (pOuter != 0) { + *ppvObj = 0; + return CLASS_E_NOAGGREGATION; + } + + // Execute Tcl script to create a servant. The script should return the + // name of a Tcl command which implements the object's operations. + TclObject servant; + int completionCode = eval(m_constructor, &servant); + if (completionCode != TCL_OK) { + *ppvObj = 0; + return E_UNEXPECTED; + } + + // Create a COM object and tie its implementation to the servant. + ComObject *pComObject = ComObject::newInstance( + m_interfaces, + m_interp, + servant, + m_destructor); + + if (m_registerActiveObject) { + pComObject->registerActiveObject(m_clsid); + } + + return pComObject->unknown()->QueryInterface(iid, ppvObj); +} + +STDMETHODIMP +ComObjectFactory::LockServer (BOOL lock) +{ + if (lock) { + ComModule::instance().lock(); + } else { + ComModule::instance().unlock(); + } + return S_OK; +} + + +SingletonObjectFactory::SingletonObjectFactory ( + const Class::Interfaces &interfaces, + Tcl_Interp *interp, + TclObject constructor, + TclObject destructor, + bool registerActiveObject): + ComObjectFactory( + interfaces, + interp, + constructor, + destructor, + registerActiveObject), + m_pInstance(0) +{ } + +SingletonObjectFactory::~SingletonObjectFactory () +{ + if (m_pInstance != 0) { + m_pInstance->Release(); + } +} + +STDMETHODIMP +SingletonObjectFactory::CreateInstance (IUnknown *pOuter, + REFIID iid, + void **ppvObj) +{ + if (m_pInstance == 0) { + LOCK_MUTEX(m_mutex) + if (m_pInstance == 0) { + HRESULT hr = ComObjectFactory::CreateInstance( + pOuter, + iid, + reinterpret_cast(&m_pInstance)); + if (FAILED(hr)) { + return hr; + } + } + } + + return m_pInstance->QueryInterface(iid, ppvObj); +} diff --git a/src/ComObjectFactory.h b/src/ComObjectFactory.h new file mode 100644 index 0000000..6bf8e14 --- /dev/null +++ b/src/ComObjectFactory.h @@ -0,0 +1,96 @@ +// $Id: ComObjectFactory.h,v 1.11 2002/04/13 03:53:56 cthuang Exp $ +#ifndef COMOBJECTFACTORY_H +#define COMOBJECTFACTORY_H + +#include "tcomApi.h" +#include "mutex.h" +#include "TclObject.h" +#include "TypeInfo.h" + +// This is a factory of COM objects. + +class TCOM_API ComObjectFactory: public IClassFactory +{ + // reference count of the factory + long m_refCount; + + // interfaces to implement + const Class::Interfaces &m_interfaces; + + // TODO: Directly accessing the Tcl interpreter means the object must run + // in a single threaded apartment to comply with Tcl's threading rules. + + // Tcl interpreter used to execute Tcl commands + Tcl_Interp *m_interp; + + // Tcl command executed to create a servant + TclObject m_constructor; + + // Tcl command executed to destroy servant + TclObject m_destructor; + + // handle of registered class object + unsigned long m_classObjectHandle; + + // CLSID used to register active object + CLSID m_clsid; + + // true if created objects should be registered in running object table + bool m_registerActiveObject; + + // true if object factory was registered + bool m_registeredFactory; + + // Execute Tcl script. Returns Tcl completion code. + int eval(TclObject script, TclObject *pResult=0); + + // Do not allow others to copy instances of this class. + ComObjectFactory(const ComObjectFactory &); // not implemented + void operator=(const ComObjectFactory &); // not implemented + +public: + ComObjectFactory( + const Class::Interfaces &interfaces, + Tcl_Interp *interp, + TclObject constructor, + TclObject destructor, + bool registerActiveObject); + virtual ~ComObjectFactory(); + + // Register factory. + void registerFactory(REFCLSID clsid, DWORD regclsFlags); + + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj); + STDMETHOD_(ULONG, AddRef)(); + STDMETHOD_(ULONG, Release)(); + + // IClassFactory methods + STDMETHOD(CreateInstance)(IUnknown *pOuter, REFIID riid, void **ppvObj); + STDMETHOD(LockServer)(BOOL fLock); +}; + +// This factory always returns the same instance. + +class TCOM_API SingletonObjectFactory: public ComObjectFactory +{ + // singleton instance returned from factory + IUnknown *m_pInstance; + + // used to synchronize construction of singleton instance + Mutex m_mutex; + +public: + SingletonObjectFactory( + const Class::Interfaces &interfaces, + Tcl_Interp *interp, + TclObject constructor, + TclObject destructor, + bool registerActiveObject); + ~SingletonObjectFactory(); + + // Override create function. + STDMETHOD(CreateInstance)(IUnknown *pOuter, REFIID riid, void **ppvObj); +}; + +#endif diff --git a/src/Extension.cpp b/src/Extension.cpp new file mode 100644 index 0000000..ab53fba --- /dev/null +++ b/src/Extension.cpp @@ -0,0 +1,99 @@ +// $Id: Extension.cpp,v 1.1 2002/06/29 15:40:32 cthuang Exp $ +#pragma warning(disable: 4786) +#include "Extension.h" +#include "ComModule.h" + +Extension::Extension (Tcl_Interp *interp): + m_interp(interp), + m_comInitialized(false) +{ + // Register new internal representation types. + Tcl_RegisterObjType(&naType); + Tcl_RegisterObjType(&nullType); + Tcl_RegisterObjType(&unknownPointerType); + + // Create additional commands. + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "bind", bindCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "class", classCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "configure", configureCmd, this, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "foreach", foreachCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "import", importCmd, this, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "info", infoCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "interface", interfaceCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "method", methodCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "na", naCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "null", nullCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "object", objectCmd, this, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "property", propertyCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "ref", refCmd, this, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "shortPathName", shortPathNameCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "typelib", typelibCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "typeof", typeofCmd, 0, 0); + Tcl_CreateObjCommand( + interp, PACKAGE_NAMESPACE "unbind", unbindCmd, 0, 0); + + Tcl_CallWhenDeleted(interp, interpDeleteProc, this); + Tcl_CreateExitHandler(exitProc, this); +} + +void +Extension::interpDeleteProc (ClientData clientData, Tcl_Interp *) +{ + Tcl_DeleteExitHandler(exitProc, clientData); + delete static_cast(clientData); +} + +void +Extension::exitProc (ClientData clientData) +{ + Extension *pExtension = + static_cast(clientData); + Tcl_DontCallWhenDeleted(pExtension->m_interp, interpDeleteProc, clientData); + delete pExtension; +} + +void +Extension::initializeCom () +{ + if (!m_comInitialized) { + ComModule::instance().initializeCom(m_coinitFlags); + m_comInitialized = true; + } +} + +// This Tcl command returns the name of the argument's internal +// representation type. + +int +Extension::typeofCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "value"); + return TCL_ERROR; + } + + Tcl_ObjType *pType = objv[1]->typePtr; + char *name = (pType == 0) ? "NULL" : pType->name; + Tcl_SetResult(interp, name, TCL_STATIC); + return TCL_OK; +} diff --git a/src/Extension.h b/src/Extension.h new file mode 100644 index 0000000..de47b6c --- /dev/null +++ b/src/Extension.h @@ -0,0 +1,103 @@ +// $Id: Extension.h,v 1.1 2002/06/29 15:40:32 cthuang Exp $ +#ifndef EXTENSION_H +#define EXTENSION_H + +#include +#include +#include "tcomApi.h" +#include "HandleSupport.h" + +// package name +#define PACKAGE_NAME "tcom" + +// namespace where the package defines new commands +#define PACKAGE_NAMESPACE "::tcom::" + +class Class; +class Interface; +class InterfaceHolder; +class Reference; +class TypeLib; + +// This class implements the commands and state of an extension loaded into a +// Tcl interpreter. + +class TCOM_API Extension +{ + // interpreter associated with this object + Tcl_Interp *m_interp; + + // flags used to initialize COM + DWORD m_coinitFlags; + + // true if COM was initialized + bool m_comInitialized; + + static void interpDeleteProc(ClientData clientData, Tcl_Interp *interp); + static void exitProc(ClientData clientData); + + static int bindCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int classCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int configureCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int foreachCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int importCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int infoCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int interfaceCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int methodCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int naCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int nullCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int objectCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int propertyCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int refCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int shortPathNameCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int typelibCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int typeofCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + static int unbindCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + + // not implemented + Extension(const Extension &); + void operator=(const Extension &); + + ~Extension () + { } + +public: + Extension(Tcl_Interp *interp); + + // Set the concurrency model to be used by the current thread. + void concurrencyModel (DWORD flags) + { m_coinitFlags = flags; } + + // Get the concurrency model to be used by the current thread. + DWORD concurrencyModel () const + { return m_coinitFlags; } + + // Initialize COM if not already initialized. + void initializeCom(); + + // handle support objects + static HandleSupport interfaceHolderHandles; + static HandleSupport referenceHandles; + static HandleSupport typeLibHandles; + + // new Tcl internal representation types + static Tcl_ObjType naType; + static Tcl_ObjType nullType; + static Tcl_ObjType unknownPointerType; + + // Create a Tcl value representing a missing optional argument. + static Tcl_Obj *newNaObj(); + + // Set the Tcl result to a description of the COM error and return TCL_ERROR. + static int setComErrorResult( + Tcl_Interp *interp, _com_error &e, const char *file, int line); + + // Find class description by name. + static const Class *findClassByCmdName(Tcl_Interp *interp, Tcl_Obj *pName); + + // Find interface description by name. + static const Interface *findInterfaceByCmdName( + Tcl_Interp *interp, Tcl_Obj *pName); +}; + +#endif diff --git a/src/HandleSupport.cpp b/src/HandleSupport.cpp new file mode 100644 index 0000000..2795dcf --- /dev/null +++ b/src/HandleSupport.cpp @@ -0,0 +1,276 @@ +// $Id: HandleSupport.cpp,v 1.15 2002/05/31 04:03:06 cthuang Exp $ +#include "HandleSupport.h" +#include +#include "ThreadLocalStorage.h" + +InternalRep::InternalRep ( + Tcl_Interp *interp, + Tcl_ObjCmdProc *pCmdProc, + ClientData objClientData): + m_interp(interp), + m_clientData(objClientData), + m_handleCount(0) +{ + std::string handleName(name()); + + m_command = Tcl_CreateObjCommand( + m_interp, + const_cast(handleName.c_str()), + pCmdProc, + objClientData, + 0); + + HandleNameToRepMap::instance(interp)->insert(handleName.c_str(), this); +} + +InternalRep::~InternalRep () +{ + HandleNameToRepMap::instance(m_interp)->erase(name().c_str()); + Tcl_DeleteCommandFromToken(m_interp, m_command); +} + +std::string +InternalRep::name () const +{ + std::ostringstream oss; + oss << "::tcom::handle0x" << std::hex << this; + return oss.str(); +} + +void +InternalRep::incrHandleCount () +{ + ++m_handleCount; +} + +long +InternalRep::decrHandleCount () +{ + if (--m_handleCount == 0) { + delete this; + return 0; + } + return m_handleCount; +} + + +// This maps Tcl_Obj pointers to an internal representation. + +class ObjToRepMap +{ + Tcl_HashTable m_hashTable; + + static ThreadLocalStorage ms_tls; + + static void exitProc(ClientData); + + // not implemented + ObjToRepMap(const ObjToRepMap &); + void operator=(const ObjToRepMap &); + + ~ObjToRepMap(); + +public: + ObjToRepMap(); + static ObjToRepMap &instance(); + + void insert(Tcl_Obj *pObj, InternalRep *pRep); + InternalRep *find(Tcl_Obj *pObj); + void erase(Tcl_Obj *pObj); +}; + +ThreadLocalStorage ObjToRepMap::ms_tls; + +void +ObjToRepMap::exitProc (ClientData clientData) +{ + delete static_cast(clientData); +} + +ObjToRepMap::ObjToRepMap () +{ + Tcl_InitHashTable(&m_hashTable, TCL_ONE_WORD_KEYS); + +#ifdef TCL_THREADS + Tcl_CreateThreadExitHandler(exitProc, this); +#else + Tcl_CreateExitHandler(exitProc, 0); +#endif +} + +ObjToRepMap::~ObjToRepMap () +{ + Tcl_DeleteHashTable(&m_hashTable); +} + +ObjToRepMap & +ObjToRepMap::instance () +{ + return ms_tls.instance(); +} + +void +ObjToRepMap::insert (Tcl_Obj *pObj, InternalRep *pRep) +{ + int isNew; + Tcl_HashEntry *pEntry = Tcl_CreateHashEntry( + &m_hashTable, reinterpret_cast(pObj), &isNew); + Tcl_SetHashValue(pEntry, pRep); +} + +InternalRep * +ObjToRepMap::find (Tcl_Obj *pObj) +{ + Tcl_HashEntry *pEntry = Tcl_FindHashEntry( + &m_hashTable, reinterpret_cast(pObj)); + if (pEntry == 0) { + return 0; + } + return static_cast(Tcl_GetHashValue(pEntry)); +} + +void +ObjToRepMap::erase (Tcl_Obj *pObj) +{ + Tcl_HashEntry *pEntry = Tcl_FindHashEntry( + &m_hashTable, reinterpret_cast(pObj)); + if (pEntry != 0) { + Tcl_DeleteHashEntry(pEntry); + } +} + + +Tcl_ObjType *CmdNameType::ms_pCmdNameType; +Tcl_ObjType CmdNameType::ms_oldCmdNameType; + +Singleton CmdNameType::ms_singleton; + +CmdNameType & +CmdNameType::instance () +{ + return ms_singleton.instance(); +} + +CmdNameType::CmdNameType () +{ + // Hijack Tcl's cmdName type. + ms_pCmdNameType = Tcl_GetObjType("cmdName"); + ms_oldCmdNameType = *ms_pCmdNameType; + ms_pCmdNameType->freeIntRepProc = freeInternalRep; + ms_pCmdNameType->dupIntRepProc = dupInternalRep; + ms_pCmdNameType->updateStringProc = updateString; + ms_pCmdNameType->setFromAnyProc = setFromAny; +} + +CmdNameType::~CmdNameType () +{ + // Restore original cmdName type. + ms_pCmdNameType->freeIntRepProc = ms_oldCmdNameType.freeIntRepProc; + ms_pCmdNameType->dupIntRepProc = ms_oldCmdNameType.dupIntRepProc; + ms_pCmdNameType->updateStringProc = ms_oldCmdNameType.updateStringProc; + ms_pCmdNameType->setFromAnyProc = ms_oldCmdNameType.setFromAnyProc; +} + +void +CmdNameType::freeInternalRep (Tcl_Obj *pObj) +{ + if (pObj->refCount == 0) { + InternalRep *pRep = ObjToRepMap::instance().find(pObj); + if (pRep != 0 && pRep->decrHandleCount() == 0) { + ObjToRepMap::instance().erase(pObj); + } + } + + ms_oldCmdNameType.freeIntRepProc(pObj); +} + +void +CmdNameType::dupInternalRep (Tcl_Obj *pSrc, Tcl_Obj *pDup) +{ + // TODO: An object is duplicated if it is about to be modified. We cannot + // allow modifications on a handle. + + ms_oldCmdNameType.dupIntRepProc(pSrc, pDup); +} + +// This should never be called because the string representation should already +// be valid. + +void +CmdNameType::updateString (Tcl_Obj *pObj) +{ + ms_oldCmdNameType.updateStringProc(pObj); +} + +int +CmdNameType::setFromAny (Tcl_Interp *interp, Tcl_Obj *pObj) +{ + // Check if the string represents an existing handle. + HandleNameToRepMap *pHandleNameToRepMap = + HandleNameToRepMap::instance(interp); + if (pHandleNameToRepMap != 0) { + InternalRep *pRep = pHandleNameToRepMap->find(pObj); + if (pRep != 0) { + if (ObjToRepMap::instance().find(pObj) == 0) { + ObjToRepMap::instance().insert(pObj, pRep); + pRep->incrHandleCount(); + } + } + } + + return ms_oldCmdNameType.setFromAnyProc(interp, pObj); +} + +Tcl_Obj * +CmdNameType::newObj (Tcl_Interp *interp, InternalRep *pRep) +{ + Tcl_Obj *pObj = Tcl_NewStringObj( + const_cast(pRep->name().c_str()), -1); + Tcl_ConvertToType(interp, pObj, ms_pCmdNameType); + return pObj; +} + + +static char ASSOC_KEY[] = "tcomHandles"; + +HandleNameToRepMap::HandleNameToRepMap (Tcl_Interp *interp): + m_interp(interp) +{ + Tcl_SetAssocData(interp, ASSOC_KEY, deleteInterpProc, this); + Tcl_CreateExitHandler(exitProc, this); +} + +HandleNameToRepMap::~HandleNameToRepMap () +{ + // Clean up any left over objects. + clear(); +} + +void +HandleNameToRepMap::deleteInterpProc (ClientData clientData, Tcl_Interp *) +{ + Tcl_DeleteExitHandler(exitProc, clientData); + delete static_cast(clientData); +} + +void +HandleNameToRepMap::exitProc (ClientData clientData) +{ + HandleNameToRepMap *pHandleNameToRepMap = + static_cast(clientData); + Tcl_DeleteAssocData(pHandleNameToRepMap->m_interp, ASSOC_KEY); +} + +HandleNameToRepMap * +HandleNameToRepMap::instance (Tcl_Interp *interp) +{ + return static_cast( + Tcl_GetAssocData(interp, ASSOC_KEY, 0)); +} + +void +HandleNameToRepMap::clear () +{ + m_map.forEach(Delete()); + m_map.clear(); +} diff --git a/src/HandleSupport.h b/src/HandleSupport.h new file mode 100644 index 0000000..51ad6c0 --- /dev/null +++ b/src/HandleSupport.h @@ -0,0 +1,182 @@ +// $Id: HandleSupport.h,v 1.27 2002/04/17 21:43:07 cthuang Exp $ +#ifndef HANDLESUPPORT_H +#define HANDLESUPPORT_H + +#include +#include +#include "tcomApi.h" +#include "HashTable.h" +#include "Singleton.h" + +// This class represents an association from a handle to an application object. +// A handle maps to an object of this class. + +class TCOM_API InternalRep +{ +protected: + Tcl_Interp *m_interp; + Tcl_Command m_command; + ClientData m_clientData; + + // number of Tcl_Obj instances that are handles to this object + long m_handleCount; + +public: + InternalRep( + Tcl_Interp *interp, + Tcl_ObjCmdProc *pCmdProc, + ClientData clientData); + virtual ~InternalRep(); + + // Get handle name. + std::string name() const; + + // Get pointer to the application object. + ClientData clientData () const + { return m_clientData; } + + void incrHandleCount(); + long decrHandleCount(); +}; + +// This class extends InternalRep to associate with a specific application +// object class. The class takes ownership of the passed in application object +// and is responsible for deleting it. + +template +class AppInternalRep: public InternalRep +{ +public: + AppInternalRep ( + Tcl_Interp *interp, + Tcl_ObjCmdProc *pCmdProc, + AppType *pAppObject): + InternalRep(interp, pCmdProc, pAppObject) + { } + + virtual ~AppInternalRep(); +}; + +template +AppInternalRep::~AppInternalRep () +{ + delete reinterpret_cast(clientData()); +} + +// Handles are instances of Tcl's cmdName type which this class hijacks in +// order to map handles to application objects. + +class TCOM_API CmdNameType +{ + // pointer to Tcl cmdName type + static Tcl_ObjType *ms_pCmdNameType; + + // saved Tcl cmdName type + static Tcl_ObjType ms_oldCmdNameType; + + // Tcl type functions + static void freeInternalRep(Tcl_Obj *pObj); + static void dupInternalRep(Tcl_Obj *pSrc, Tcl_Obj *pDup); + static void updateString(Tcl_Obj *pObj); + static int setFromAny(Tcl_Interp *interp, Tcl_Obj *pObj); + + friend class Singleton; + static Singleton ms_singleton; + + CmdNameType(); + ~CmdNameType(); + +public: + // Get instance of this class. + static CmdNameType &instance(); + + // Create handle. + Tcl_Obj *newObj(Tcl_Interp *interp, InternalRep *pRep); +}; + +// Maps handle name to internal representation. There's an instance of this +// class associated with each Tcl interpreter that loads the extension. + +class TCOM_API HandleNameToRepMap +{ + Tcl_Interp *m_interp; + + // handle string representation to internal representation map + typedef StringHashTable Map; + Map m_map; + + static void deleteInterpProc(ClientData clientData, Tcl_Interp *interp); + static void exitProc(ClientData clientData); + + ~HandleNameToRepMap(); + +public: + HandleNameToRepMap(Tcl_Interp *interp); + + // Get instance associated with the Tcl interpreter. + static HandleNameToRepMap *instance(Tcl_Interp *interp); + + // Insert handle to object mapping. + void insert (const char *handleStr, InternalRep *pRep) + { m_map.insert(handleStr, pRep); } + + // Get the object represented by the handle. + InternalRep *find (Tcl_Obj *pHandle) const + { return m_map.find(Tcl_GetStringFromObj(pHandle, 0)); } + + // Remove handle to object mapping. + void erase (const char *handleStr) + { m_map.erase(handleStr); } + + // Clean all handles. + void clear(); +}; + +// This class provides functions to map handles to objects of a specific +// application class. + +template +class HandleSupport +{ + // Tcl command that implements the operations of the object + Tcl_ObjCmdProc *m_pCmdProc; + +public: + HandleSupport (Tcl_ObjCmdProc *pCmdProc): + m_pCmdProc(pCmdProc) + { } + + // Create a handle and associate it with an application object. Takes + // ownership of the application object and is responsible for deleting it. + Tcl_Obj *newObj(Tcl_Interp *interp, AppType *pAppObject); + + // Get count of matching elements. + size_t count (Tcl_Interp *interp, Tcl_Obj *pHandle) const + { return HandleNameToRepMap::instance(interp)->count(pHandle); } + + // Get the application object represented by the handle. If the handle + // is invalid, return 0. + AppType *find(Tcl_Interp *interp, Tcl_Obj *pHandle) const; +}; + +template +Tcl_Obj * +HandleSupport::newObj (Tcl_Interp *interp, AppType *pAppObject) +{ + AppInternalRep *pRep = new AppInternalRep( + interp, m_pCmdProc, pAppObject); + return CmdNameType::instance().newObj(interp, pRep); +} + +template +AppType * +HandleSupport::find (Tcl_Interp *interp, Tcl_Obj *pObj) const +{ + InternalRep *pRep = HandleNameToRepMap::instance(interp)->find(pObj); + if (pRep == 0) { + return 0; + } + return reinterpret_cast(pRep->clientData()); +} + +#endif diff --git a/src/HashTable.h b/src/HashTable.h new file mode 100644 index 0000000..8b1d3d3 --- /dev/null +++ b/src/HashTable.h @@ -0,0 +1,176 @@ +// $Id: HashTable.h,v 1.21 2002/04/13 03:53:56 cthuang Exp $ +#ifndef HASHTABLE_H +#define HASHTABLE_H + +#include + +// Function object that invokes delete on its argument + +struct Delete +{ + template + void operator() (T p) const + { delete p; } +}; + +// This is a base class used to implement hash tables. + +template +class BasicHashTable +{ +protected: + Tcl_HashTable m_hashTable; + +public: + BasicHashTable (int keyType) + { Tcl_InitHashTable(&m_hashTable, keyType); } + + ~BasicHashTable () + { Tcl_DeleteHashTable(&m_hashTable); } + + // Remove all elements. + void clear(); + + // Call function on all data elements. + template + void forEach (F f) + { + Tcl_HashSearch search; + Tcl_HashEntry *pEntry = Tcl_FirstHashEntry(&m_hashTable, &search); + while (pEntry != 0) { + Tcl_HashEntry *pNext = Tcl_NextHashEntry(&search); + f(reinterpret_cast(Tcl_GetHashValue(pEntry))); + pEntry = pNext; + } + } +}; + +template +void +BasicHashTable::clear () +{ + Tcl_HashSearch search; + Tcl_HashEntry *pEntry = Tcl_FirstHashEntry(&m_hashTable, &search); + while (pEntry != 0) { + Tcl_HashEntry *pNext = Tcl_NextHashEntry(&search); + Tcl_DeleteHashEntry(pEntry); + pEntry = pNext; + } +} + +// This class wraps a Tcl hash table that uses structures as keys. The mapped +// type is assumed to be a pointer type. + +template +class HashTable: public BasicHashTable +{ +public: + typedef K key_type; + typedef D mapped_type; + + HashTable (): BasicHashTable(sizeof(K) / sizeof(int)) + { } + + // Insert data into table. + void insert(const K &key, D data); + + // Find data in table. + D find(const K &key) const; + + // Remove data. + void erase(const K &key); +}; + +template +void +HashTable::insert (const K &key, D value) +{ + int isNew; + Tcl_HashEntry *pEntry = Tcl_CreateHashEntry( + &m_hashTable, + reinterpret_cast(&key), + &isNew); + Tcl_SetHashValue(pEntry, reinterpret_cast(value)); +} + +template +D +HashTable::find (const K &key) const +{ + Tcl_HashEntry *pEntry = Tcl_FindHashEntry( + const_cast(&m_hashTable), + reinterpret_cast(&key)); + if (pEntry == 0) { + return 0; + } + return reinterpret_cast(Tcl_GetHashValue(pEntry)); +} + +template +void +HashTable::erase (const K &key) +{ + Tcl_HashEntry *pEntry = Tcl_FindHashEntry( + &m_hashTable, + reinterpret_cast(&key)); + if (pEntry != 0) { + Tcl_DeleteHashEntry(pEntry); + } +} + +// This class wraps a Tcl hash table that uses null-terminated strings as keys. +// The mapped type is assumed to be a pointer type. + +template +class StringHashTable: public BasicHashTable +{ +public: + typedef const char *key_type; + typedef D mapped_type; + + StringHashTable (): BasicHashTable(TCL_STRING_KEYS) + { } + + void insert(const char *key, D value); + D find(const char *key) const; + void erase(const char *key); +}; + +template +void +StringHashTable::insert (const char *key, D value) +{ + int isNew; + Tcl_HashEntry *pEntry = Tcl_CreateHashEntry( + &m_hashTable, + const_cast(key), + &isNew); + Tcl_SetHashValue(pEntry, reinterpret_cast(value)); +} + +template +D +StringHashTable::find (const char *key) const +{ + Tcl_HashEntry *pEntry = Tcl_FindHashEntry( + const_cast(&m_hashTable), + const_cast(key)); + if (pEntry == 0) { + return 0; + } + return reinterpret_cast(Tcl_GetHashValue(pEntry)); +} + +template +void +StringHashTable::erase (const char *key) +{ + Tcl_HashEntry *pEntry = Tcl_FindHashEntry( + &m_hashTable, + const_cast(key)); + if (pEntry != 0) { + Tcl_DeleteHashEntry(pEntry); + } +} + +#endif diff --git a/src/InterfaceAdapter.cpp b/src/InterfaceAdapter.cpp new file mode 100644 index 0000000..f1117e3 --- /dev/null +++ b/src/InterfaceAdapter.cpp @@ -0,0 +1,145 @@ +// $Id: InterfaceAdapter.cpp,v 1.3 2002/02/27 01:58:45 cthuang Exp $ +#pragma warning(disable: 4786) +#include "ComObject.h" +#include "InterfaceAdapter.h" + +InterfaceAdapter::InterfaceAdapter ( + ComObject &object, + const Interface &interfaceDesc, + bool forceDispatch): + m_object(object), + m_interface(interfaceDesc) +{ + // Initialize virtual function index to method description map. + const Interface::Methods &methods = m_interface.methods(); + for (Interface::Methods::const_iterator p = methods.begin(); + p != methods.end(); ++p) { + m_vtblIndexToMethodMap.insert(VtblIndexToMethodMap::value_type( + p->vtblIndex(), &(*p))); + } + + if (m_interface.dispatchable() || forceDispatch) { + m_pVtbl = dispatchVtbl; + + // Initialize dispatch member ID to method description map. + const Interface::Methods &methods = m_interface.methods(); + for (Interface::Methods::const_iterator pMethod = methods.begin(); + pMethod != methods.end(); ++pMethod) { + m_dispIdToMethodMap.insert(DispIdToMethodMap::value_type( + pMethod->memberid(), &(*pMethod))); + } + + // Initialize set of property dispatch member ID's. + const Interface::Properties &properties = m_interface.properties(); + for (Interface::Properties::const_iterator pProp = properties.begin(); + pProp != properties.end(); ++pProp) { + m_propertyDispIds.insert(pProp->memberid()); + } + + } else { + m_pVtbl = unknownVtbl; + } +} + +const Method * +InterfaceAdapter::findComMethod (int funcIndex) +{ + VtblIndexToMethodMap::const_iterator p = + m_vtblIndexToMethodMap.find(funcIndex); + if (p == m_vtblIndexToMethodMap.end()) { + return 0; + } + return p->second; +} + +const Method * +InterfaceAdapter::findDispatchMethod (DISPID dispid) +{ + DispIdToMethodMap::const_iterator p = m_dispIdToMethodMap.find(dispid); + if (p == m_dispIdToMethodMap.end()) { + return 0; + } + return p->second; +} + +// Implement IUnknown methods + +STDMETHODIMP +InterfaceAdapter::QueryInterface ( + InterfaceAdapter *pThis, REFIID iid, void **ppvObj) +{ + return pThis->m_object.queryInterface(iid, ppvObj); +} + +STDMETHODIMP_(ULONG) +InterfaceAdapter::AddRef (InterfaceAdapter *pThis) +{ + return pThis->m_object.addRef(); +} + +STDMETHODIMP_(ULONG) +InterfaceAdapter::Release (InterfaceAdapter *pThis) +{ + return pThis->m_object.release(); +} + +// Implement IDispatch methods + +STDMETHODIMP +InterfaceAdapter::GetTypeInfoCount (InterfaceAdapter *, UINT *pCount) +{ + *pCount = 1; + return S_OK; +} + +STDMETHODIMP +InterfaceAdapter::GetTypeInfo ( + InterfaceAdapter *pThis, UINT index, LCID, ITypeInfo **ppTypeInfo) +{ + if (index != 0) { + *ppTypeInfo = 0; + return DISP_E_BADINDEX; + } + + ITypeInfo *pTypeInfo = pThis->m_interface.typeInfo(); + pTypeInfo->AddRef(); + *ppTypeInfo = pTypeInfo; + return S_OK; +} + +STDMETHODIMP +InterfaceAdapter::GetIDsOfNames ( + InterfaceAdapter *pThis, + REFIID, + OLECHAR **rgszNames, + UINT cNames, + LCID, + DISPID *rgDispId) +{ + ITypeInfo *pTypeInfo = pThis->m_interface.typeInfo(); + return pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId); +} + +STDMETHODIMP +InterfaceAdapter::Invoke ( + InterfaceAdapter *pThis, + DISPID dispid, + REFIID iid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *pArgErr) +{ + return pThis->m_object.invoke( + pThis, + dispid, + iid, + lcid, + wFlags, + pDispParams, + pVarResult, + pExcepInfo, + pArgErr); +} diff --git a/src/InterfaceAdapter.h b/src/InterfaceAdapter.h new file mode 100644 index 0000000..19bc8e0 --- /dev/null +++ b/src/InterfaceAdapter.h @@ -0,0 +1,101 @@ +// $Id: InterfaceAdapter.h,v 1.3 2002/02/27 01:58:45 cthuang Exp $ +#ifndef INTERFACEADAPTER_H +#define INTERFACEADAPTER_H + +#include +#include +#include "tcomApi.h" +#include "TypeInfo.h" + +class TCOM_API ComObject; + +// This class implements an interface for COM clients to invoke functions +// through a virtual function table. It delegates the operations to the +// ComObject class. + +class TCOM_API InterfaceAdapter +{ + // We rely on the knowledge that the C++ compiler implements objects having + // virtual functions by storing a pointer to a virtual function table + // at the beginning of the object. We simulate such an object by defining + // a class with a virtual function table pointer as the first data member. + const void *m_pVtbl; + + // delegate operations to this object + ComObject &m_object; + + // description of the interface to implement + const Interface &m_interface; + + // virtual function index to method description map + typedef std::map VtblIndexToMethodMap; + VtblIndexToMethodMap m_vtblIndexToMethodMap; + + // dispatch member ID to method description map + typedef std::map DispIdToMethodMap; + DispIdToMethodMap m_dispIdToMethodMap; + + // dispatch member ID's which are actually properties + typedef std::set DispIdSet; + DispIdSet m_propertyDispIds; + + // virtual function table for IUnknown derived interfaces + static const void *unknownVtbl[]; + + // virtual function table for IDispatch derived interfaces + static const void *dispatchVtbl[]; + + InterfaceAdapter(const InterfaceAdapter &); // not implemented + void operator=(const InterfaceAdapter &); // not implemented + +public: + InterfaceAdapter( + ComObject &object, + const Interface &interfaceDesc, + bool forceDispatch=false); + + // Get delegate object. + ComObject &object () const + { return m_object; } + + // Get COM method description. + const Method *findComMethod(int funcIndex); + + // Get dispatch method description. + const Method *findDispatchMethod(DISPID dispid); + + // Return true if the dispatch member ID identifies a property. + bool isProperty (DISPID dispid) const + { return m_propertyDispIds.count(dispid) != 0; } + + // IUnknown implementation + static STDMETHODIMP QueryInterface( + InterfaceAdapter *pThis, REFIID iid, void **ppvObj); + static STDMETHODIMP_(ULONG) AddRef(InterfaceAdapter *pThis); + static STDMETHODIMP_(ULONG) Release(InterfaceAdapter *pThis); + + // IDispatch implementation + static STDMETHODIMP GetTypeInfoCount( + InterfaceAdapter *pThis, UINT *pctinfo); + static STDMETHODIMP GetTypeInfo( + InterfaceAdapter *pThis, UINT itinfo, LCID lcid, ITypeInfo **pptinfo); + static STDMETHODIMP GetIDsOfNames( + InterfaceAdapter *pThis, + REFIID iid, + OLECHAR **rgszNames, + UINT cNames, + LCID lcid, + DISPID *rgdispid); + static STDMETHODIMP Invoke( + InterfaceAdapter *pThis, + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pdispparams, + VARIANT *pvarResult, + EXCEPINFO *pexcepinfo, + UINT *puArgErr); +}; + +#endif diff --git a/src/InterfaceAdapterVtbl.cpp b/src/InterfaceAdapterVtbl.cpp new file mode 100644 index 0000000..896f36d --- /dev/null +++ b/src/InterfaceAdapterVtbl.cpp @@ -0,0 +1,3131 @@ +// $Id: InterfaceAdapterVtbl.cpp,v 1.3 2001/10/13 17:56:14 Administrator Exp $ +#pragma warning(disable: 4786) +#include "InterfaceAdapter.h" +#include "ComObject.h" + +#pragma code_seg(".orpc") + +static __declspec(naked) void +delegate (void) +{ + __asm { + push ebp // set up simple stack frame + mov ebp, esp + + sub esp, 8 // set up local variables + // localVar(hrFromInvoke) + // localVar(pArgEnd) + // ebp + // funcIndex + // retAddr + // this + // args + + call invokeComObjectFunction + + // The following code adjusts the stack and returns to the caller. + // This involves copying the return address and the HRESULT + // to the bottom of the stack frame, adjusting the stack + // pointer, and returning to the caller. + push esi + mov esi, [ebp-4] // esi = address after last argument + + sub esi, 4 // esi points to bottom arg on stack + mov eax, [ebp+8] // copy retaddr down + mov [esi], eax + + sub esi, 4 + mov eax, [ebp-8] // copy hrFromInvoke down + mov [esi], eax + + mov eax, esi // reset stack and return to caller + pop esi + mov ebp, [ebp] + mov esp, eax + pop eax + ret + } +} + +#define FUNCTION_ENTRY_POINT(n) \ +static void __declspec(naked) function_##n(void) \ +{ __asm push (n) __asm jmp delegate } + +// 0 QueryInterface +// 1 AddRef +// 2 Release +FUNCTION_ENTRY_POINT(3) +FUNCTION_ENTRY_POINT(4) +FUNCTION_ENTRY_POINT(5) +FUNCTION_ENTRY_POINT(6) +FUNCTION_ENTRY_POINT(7) +FUNCTION_ENTRY_POINT(8) +FUNCTION_ENTRY_POINT(9) +FUNCTION_ENTRY_POINT(10) +FUNCTION_ENTRY_POINT(11) +FUNCTION_ENTRY_POINT(12) +FUNCTION_ENTRY_POINT(13) +FUNCTION_ENTRY_POINT(14) +FUNCTION_ENTRY_POINT(15) +FUNCTION_ENTRY_POINT(16) +FUNCTION_ENTRY_POINT(17) +FUNCTION_ENTRY_POINT(18) +FUNCTION_ENTRY_POINT(19) +FUNCTION_ENTRY_POINT(20) +FUNCTION_ENTRY_POINT(21) +FUNCTION_ENTRY_POINT(22) +FUNCTION_ENTRY_POINT(23) +FUNCTION_ENTRY_POINT(24) +FUNCTION_ENTRY_POINT(25) +FUNCTION_ENTRY_POINT(26) +FUNCTION_ENTRY_POINT(27) +FUNCTION_ENTRY_POINT(28) +FUNCTION_ENTRY_POINT(29) +FUNCTION_ENTRY_POINT(30) +FUNCTION_ENTRY_POINT(31) +FUNCTION_ENTRY_POINT(32) +FUNCTION_ENTRY_POINT(33) +FUNCTION_ENTRY_POINT(34) +FUNCTION_ENTRY_POINT(35) +FUNCTION_ENTRY_POINT(36) +FUNCTION_ENTRY_POINT(37) +FUNCTION_ENTRY_POINT(38) +FUNCTION_ENTRY_POINT(39) +FUNCTION_ENTRY_POINT(40) +FUNCTION_ENTRY_POINT(41) +FUNCTION_ENTRY_POINT(42) +FUNCTION_ENTRY_POINT(43) +FUNCTION_ENTRY_POINT(44) +FUNCTION_ENTRY_POINT(45) +FUNCTION_ENTRY_POINT(46) +FUNCTION_ENTRY_POINT(47) +FUNCTION_ENTRY_POINT(48) +FUNCTION_ENTRY_POINT(49) +FUNCTION_ENTRY_POINT(50) +FUNCTION_ENTRY_POINT(51) +FUNCTION_ENTRY_POINT(52) +FUNCTION_ENTRY_POINT(53) +FUNCTION_ENTRY_POINT(54) +FUNCTION_ENTRY_POINT(55) +FUNCTION_ENTRY_POINT(56) +FUNCTION_ENTRY_POINT(57) +FUNCTION_ENTRY_POINT(58) +FUNCTION_ENTRY_POINT(59) +FUNCTION_ENTRY_POINT(60) +FUNCTION_ENTRY_POINT(61) +FUNCTION_ENTRY_POINT(62) +FUNCTION_ENTRY_POINT(63) +FUNCTION_ENTRY_POINT(64) +FUNCTION_ENTRY_POINT(65) +FUNCTION_ENTRY_POINT(66) +FUNCTION_ENTRY_POINT(67) +FUNCTION_ENTRY_POINT(68) +FUNCTION_ENTRY_POINT(69) +FUNCTION_ENTRY_POINT(70) +FUNCTION_ENTRY_POINT(71) +FUNCTION_ENTRY_POINT(72) +FUNCTION_ENTRY_POINT(73) +FUNCTION_ENTRY_POINT(74) +FUNCTION_ENTRY_POINT(75) +FUNCTION_ENTRY_POINT(76) +FUNCTION_ENTRY_POINT(77) +FUNCTION_ENTRY_POINT(78) +FUNCTION_ENTRY_POINT(79) +FUNCTION_ENTRY_POINT(80) +FUNCTION_ENTRY_POINT(81) +FUNCTION_ENTRY_POINT(82) +FUNCTION_ENTRY_POINT(83) +FUNCTION_ENTRY_POINT(84) +FUNCTION_ENTRY_POINT(85) +FUNCTION_ENTRY_POINT(86) +FUNCTION_ENTRY_POINT(87) +FUNCTION_ENTRY_POINT(88) +FUNCTION_ENTRY_POINT(89) +FUNCTION_ENTRY_POINT(90) +FUNCTION_ENTRY_POINT(91) +FUNCTION_ENTRY_POINT(92) +FUNCTION_ENTRY_POINT(93) +FUNCTION_ENTRY_POINT(94) +FUNCTION_ENTRY_POINT(95) +FUNCTION_ENTRY_POINT(96) +FUNCTION_ENTRY_POINT(97) +FUNCTION_ENTRY_POINT(98) +FUNCTION_ENTRY_POINT(99) +FUNCTION_ENTRY_POINT(100) +FUNCTION_ENTRY_POINT(101) +FUNCTION_ENTRY_POINT(102) +FUNCTION_ENTRY_POINT(103) +FUNCTION_ENTRY_POINT(104) +FUNCTION_ENTRY_POINT(105) +FUNCTION_ENTRY_POINT(106) +FUNCTION_ENTRY_POINT(107) +FUNCTION_ENTRY_POINT(108) +FUNCTION_ENTRY_POINT(109) +FUNCTION_ENTRY_POINT(110) +FUNCTION_ENTRY_POINT(111) +FUNCTION_ENTRY_POINT(112) +FUNCTION_ENTRY_POINT(113) +FUNCTION_ENTRY_POINT(114) +FUNCTION_ENTRY_POINT(115) +FUNCTION_ENTRY_POINT(116) +FUNCTION_ENTRY_POINT(117) +FUNCTION_ENTRY_POINT(118) +FUNCTION_ENTRY_POINT(119) +FUNCTION_ENTRY_POINT(120) +FUNCTION_ENTRY_POINT(121) +FUNCTION_ENTRY_POINT(122) +FUNCTION_ENTRY_POINT(123) +FUNCTION_ENTRY_POINT(124) +FUNCTION_ENTRY_POINT(125) +FUNCTION_ENTRY_POINT(126) +FUNCTION_ENTRY_POINT(127) +FUNCTION_ENTRY_POINT(128) +FUNCTION_ENTRY_POINT(129) +FUNCTION_ENTRY_POINT(130) +FUNCTION_ENTRY_POINT(131) +FUNCTION_ENTRY_POINT(132) +FUNCTION_ENTRY_POINT(133) +FUNCTION_ENTRY_POINT(134) +FUNCTION_ENTRY_POINT(135) +FUNCTION_ENTRY_POINT(136) +FUNCTION_ENTRY_POINT(137) +FUNCTION_ENTRY_POINT(138) +FUNCTION_ENTRY_POINT(139) +FUNCTION_ENTRY_POINT(140) +FUNCTION_ENTRY_POINT(141) +FUNCTION_ENTRY_POINT(142) +FUNCTION_ENTRY_POINT(143) +FUNCTION_ENTRY_POINT(144) +FUNCTION_ENTRY_POINT(145) +FUNCTION_ENTRY_POINT(146) +FUNCTION_ENTRY_POINT(147) +FUNCTION_ENTRY_POINT(148) +FUNCTION_ENTRY_POINT(149) +FUNCTION_ENTRY_POINT(150) +FUNCTION_ENTRY_POINT(151) +FUNCTION_ENTRY_POINT(152) +FUNCTION_ENTRY_POINT(153) +FUNCTION_ENTRY_POINT(154) +FUNCTION_ENTRY_POINT(155) +FUNCTION_ENTRY_POINT(156) +FUNCTION_ENTRY_POINT(157) +FUNCTION_ENTRY_POINT(158) +FUNCTION_ENTRY_POINT(159) +FUNCTION_ENTRY_POINT(160) +FUNCTION_ENTRY_POINT(161) +FUNCTION_ENTRY_POINT(162) +FUNCTION_ENTRY_POINT(163) +FUNCTION_ENTRY_POINT(164) +FUNCTION_ENTRY_POINT(165) +FUNCTION_ENTRY_POINT(166) +FUNCTION_ENTRY_POINT(167) +FUNCTION_ENTRY_POINT(168) +FUNCTION_ENTRY_POINT(169) +FUNCTION_ENTRY_POINT(170) +FUNCTION_ENTRY_POINT(171) +FUNCTION_ENTRY_POINT(172) +FUNCTION_ENTRY_POINT(173) +FUNCTION_ENTRY_POINT(174) +FUNCTION_ENTRY_POINT(175) +FUNCTION_ENTRY_POINT(176) +FUNCTION_ENTRY_POINT(177) +FUNCTION_ENTRY_POINT(178) +FUNCTION_ENTRY_POINT(179) +FUNCTION_ENTRY_POINT(180) +FUNCTION_ENTRY_POINT(181) +FUNCTION_ENTRY_POINT(182) +FUNCTION_ENTRY_POINT(183) +FUNCTION_ENTRY_POINT(184) +FUNCTION_ENTRY_POINT(185) +FUNCTION_ENTRY_POINT(186) +FUNCTION_ENTRY_POINT(187) +FUNCTION_ENTRY_POINT(188) +FUNCTION_ENTRY_POINT(189) +FUNCTION_ENTRY_POINT(190) +FUNCTION_ENTRY_POINT(191) +FUNCTION_ENTRY_POINT(192) +FUNCTION_ENTRY_POINT(193) +FUNCTION_ENTRY_POINT(194) +FUNCTION_ENTRY_POINT(195) +FUNCTION_ENTRY_POINT(196) +FUNCTION_ENTRY_POINT(197) +FUNCTION_ENTRY_POINT(198) +FUNCTION_ENTRY_POINT(199) +FUNCTION_ENTRY_POINT(200) +FUNCTION_ENTRY_POINT(201) +FUNCTION_ENTRY_POINT(202) +FUNCTION_ENTRY_POINT(203) +FUNCTION_ENTRY_POINT(204) +FUNCTION_ENTRY_POINT(205) +FUNCTION_ENTRY_POINT(206) +FUNCTION_ENTRY_POINT(207) +FUNCTION_ENTRY_POINT(208) +FUNCTION_ENTRY_POINT(209) +FUNCTION_ENTRY_POINT(210) +FUNCTION_ENTRY_POINT(211) +FUNCTION_ENTRY_POINT(212) +FUNCTION_ENTRY_POINT(213) +FUNCTION_ENTRY_POINT(214) +FUNCTION_ENTRY_POINT(215) +FUNCTION_ENTRY_POINT(216) +FUNCTION_ENTRY_POINT(217) +FUNCTION_ENTRY_POINT(218) +FUNCTION_ENTRY_POINT(219) +FUNCTION_ENTRY_POINT(220) +FUNCTION_ENTRY_POINT(221) +FUNCTION_ENTRY_POINT(222) +FUNCTION_ENTRY_POINT(223) +FUNCTION_ENTRY_POINT(224) +FUNCTION_ENTRY_POINT(225) +FUNCTION_ENTRY_POINT(226) +FUNCTION_ENTRY_POINT(227) +FUNCTION_ENTRY_POINT(228) +FUNCTION_ENTRY_POINT(229) +FUNCTION_ENTRY_POINT(230) +FUNCTION_ENTRY_POINT(231) +FUNCTION_ENTRY_POINT(232) +FUNCTION_ENTRY_POINT(233) +FUNCTION_ENTRY_POINT(234) +FUNCTION_ENTRY_POINT(235) +FUNCTION_ENTRY_POINT(236) +FUNCTION_ENTRY_POINT(237) +FUNCTION_ENTRY_POINT(238) +FUNCTION_ENTRY_POINT(239) +FUNCTION_ENTRY_POINT(240) +FUNCTION_ENTRY_POINT(241) +FUNCTION_ENTRY_POINT(242) +FUNCTION_ENTRY_POINT(243) +FUNCTION_ENTRY_POINT(244) +FUNCTION_ENTRY_POINT(245) +FUNCTION_ENTRY_POINT(246) +FUNCTION_ENTRY_POINT(247) +FUNCTION_ENTRY_POINT(248) +FUNCTION_ENTRY_POINT(249) +FUNCTION_ENTRY_POINT(250) +FUNCTION_ENTRY_POINT(251) +FUNCTION_ENTRY_POINT(252) +FUNCTION_ENTRY_POINT(253) +FUNCTION_ENTRY_POINT(254) +FUNCTION_ENTRY_POINT(255) +FUNCTION_ENTRY_POINT(256) +FUNCTION_ENTRY_POINT(257) +FUNCTION_ENTRY_POINT(258) +FUNCTION_ENTRY_POINT(259) +FUNCTION_ENTRY_POINT(260) +FUNCTION_ENTRY_POINT(261) +FUNCTION_ENTRY_POINT(262) +FUNCTION_ENTRY_POINT(263) +FUNCTION_ENTRY_POINT(264) +FUNCTION_ENTRY_POINT(265) +FUNCTION_ENTRY_POINT(266) +FUNCTION_ENTRY_POINT(267) +FUNCTION_ENTRY_POINT(268) +FUNCTION_ENTRY_POINT(269) +FUNCTION_ENTRY_POINT(270) +FUNCTION_ENTRY_POINT(271) +FUNCTION_ENTRY_POINT(272) +FUNCTION_ENTRY_POINT(273) +FUNCTION_ENTRY_POINT(274) +FUNCTION_ENTRY_POINT(275) +FUNCTION_ENTRY_POINT(276) +FUNCTION_ENTRY_POINT(277) +FUNCTION_ENTRY_POINT(278) +FUNCTION_ENTRY_POINT(279) +FUNCTION_ENTRY_POINT(280) +FUNCTION_ENTRY_POINT(281) +FUNCTION_ENTRY_POINT(282) +FUNCTION_ENTRY_POINT(283) +FUNCTION_ENTRY_POINT(284) +FUNCTION_ENTRY_POINT(285) +FUNCTION_ENTRY_POINT(286) +FUNCTION_ENTRY_POINT(287) +FUNCTION_ENTRY_POINT(288) +FUNCTION_ENTRY_POINT(289) +FUNCTION_ENTRY_POINT(290) +FUNCTION_ENTRY_POINT(291) +FUNCTION_ENTRY_POINT(292) +FUNCTION_ENTRY_POINT(293) +FUNCTION_ENTRY_POINT(294) +FUNCTION_ENTRY_POINT(295) +FUNCTION_ENTRY_POINT(296) +FUNCTION_ENTRY_POINT(297) +FUNCTION_ENTRY_POINT(298) +FUNCTION_ENTRY_POINT(299) +FUNCTION_ENTRY_POINT(300) +FUNCTION_ENTRY_POINT(301) +FUNCTION_ENTRY_POINT(302) +FUNCTION_ENTRY_POINT(303) +FUNCTION_ENTRY_POINT(304) +FUNCTION_ENTRY_POINT(305) +FUNCTION_ENTRY_POINT(306) +FUNCTION_ENTRY_POINT(307) +FUNCTION_ENTRY_POINT(308) +FUNCTION_ENTRY_POINT(309) +FUNCTION_ENTRY_POINT(310) +FUNCTION_ENTRY_POINT(311) +FUNCTION_ENTRY_POINT(312) +FUNCTION_ENTRY_POINT(313) +FUNCTION_ENTRY_POINT(314) +FUNCTION_ENTRY_POINT(315) +FUNCTION_ENTRY_POINT(316) +FUNCTION_ENTRY_POINT(317) +FUNCTION_ENTRY_POINT(318) +FUNCTION_ENTRY_POINT(319) +FUNCTION_ENTRY_POINT(320) +FUNCTION_ENTRY_POINT(321) +FUNCTION_ENTRY_POINT(322) +FUNCTION_ENTRY_POINT(323) +FUNCTION_ENTRY_POINT(324) +FUNCTION_ENTRY_POINT(325) +FUNCTION_ENTRY_POINT(326) +FUNCTION_ENTRY_POINT(327) +FUNCTION_ENTRY_POINT(328) +FUNCTION_ENTRY_POINT(329) +FUNCTION_ENTRY_POINT(330) +FUNCTION_ENTRY_POINT(331) +FUNCTION_ENTRY_POINT(332) +FUNCTION_ENTRY_POINT(333) +FUNCTION_ENTRY_POINT(334) +FUNCTION_ENTRY_POINT(335) +FUNCTION_ENTRY_POINT(336) +FUNCTION_ENTRY_POINT(337) +FUNCTION_ENTRY_POINT(338) +FUNCTION_ENTRY_POINT(339) +FUNCTION_ENTRY_POINT(340) +FUNCTION_ENTRY_POINT(341) +FUNCTION_ENTRY_POINT(342) +FUNCTION_ENTRY_POINT(343) +FUNCTION_ENTRY_POINT(344) +FUNCTION_ENTRY_POINT(345) +FUNCTION_ENTRY_POINT(346) +FUNCTION_ENTRY_POINT(347) +FUNCTION_ENTRY_POINT(348) +FUNCTION_ENTRY_POINT(349) +FUNCTION_ENTRY_POINT(350) +FUNCTION_ENTRY_POINT(351) +FUNCTION_ENTRY_POINT(352) +FUNCTION_ENTRY_POINT(353) +FUNCTION_ENTRY_POINT(354) +FUNCTION_ENTRY_POINT(355) +FUNCTION_ENTRY_POINT(356) +FUNCTION_ENTRY_POINT(357) +FUNCTION_ENTRY_POINT(358) +FUNCTION_ENTRY_POINT(359) +FUNCTION_ENTRY_POINT(360) +FUNCTION_ENTRY_POINT(361) +FUNCTION_ENTRY_POINT(362) +FUNCTION_ENTRY_POINT(363) +FUNCTION_ENTRY_POINT(364) +FUNCTION_ENTRY_POINT(365) +FUNCTION_ENTRY_POINT(366) +FUNCTION_ENTRY_POINT(367) +FUNCTION_ENTRY_POINT(368) +FUNCTION_ENTRY_POINT(369) +FUNCTION_ENTRY_POINT(370) +FUNCTION_ENTRY_POINT(371) +FUNCTION_ENTRY_POINT(372) +FUNCTION_ENTRY_POINT(373) +FUNCTION_ENTRY_POINT(374) +FUNCTION_ENTRY_POINT(375) +FUNCTION_ENTRY_POINT(376) +FUNCTION_ENTRY_POINT(377) +FUNCTION_ENTRY_POINT(378) +FUNCTION_ENTRY_POINT(379) +FUNCTION_ENTRY_POINT(380) +FUNCTION_ENTRY_POINT(381) +FUNCTION_ENTRY_POINT(382) +FUNCTION_ENTRY_POINT(383) +FUNCTION_ENTRY_POINT(384) +FUNCTION_ENTRY_POINT(385) +FUNCTION_ENTRY_POINT(386) +FUNCTION_ENTRY_POINT(387) +FUNCTION_ENTRY_POINT(388) +FUNCTION_ENTRY_POINT(389) +FUNCTION_ENTRY_POINT(390) +FUNCTION_ENTRY_POINT(391) +FUNCTION_ENTRY_POINT(392) +FUNCTION_ENTRY_POINT(393) +FUNCTION_ENTRY_POINT(394) +FUNCTION_ENTRY_POINT(395) +FUNCTION_ENTRY_POINT(396) +FUNCTION_ENTRY_POINT(397) +FUNCTION_ENTRY_POINT(398) +FUNCTION_ENTRY_POINT(399) +FUNCTION_ENTRY_POINT(400) +FUNCTION_ENTRY_POINT(401) +FUNCTION_ENTRY_POINT(402) +FUNCTION_ENTRY_POINT(403) +FUNCTION_ENTRY_POINT(404) +FUNCTION_ENTRY_POINT(405) +FUNCTION_ENTRY_POINT(406) +FUNCTION_ENTRY_POINT(407) +FUNCTION_ENTRY_POINT(408) +FUNCTION_ENTRY_POINT(409) +FUNCTION_ENTRY_POINT(410) +FUNCTION_ENTRY_POINT(411) +FUNCTION_ENTRY_POINT(412) +FUNCTION_ENTRY_POINT(413) +FUNCTION_ENTRY_POINT(414) +FUNCTION_ENTRY_POINT(415) +FUNCTION_ENTRY_POINT(416) +FUNCTION_ENTRY_POINT(417) +FUNCTION_ENTRY_POINT(418) +FUNCTION_ENTRY_POINT(419) +FUNCTION_ENTRY_POINT(420) +FUNCTION_ENTRY_POINT(421) +FUNCTION_ENTRY_POINT(422) +FUNCTION_ENTRY_POINT(423) +FUNCTION_ENTRY_POINT(424) +FUNCTION_ENTRY_POINT(425) +FUNCTION_ENTRY_POINT(426) +FUNCTION_ENTRY_POINT(427) +FUNCTION_ENTRY_POINT(428) +FUNCTION_ENTRY_POINT(429) +FUNCTION_ENTRY_POINT(430) +FUNCTION_ENTRY_POINT(431) +FUNCTION_ENTRY_POINT(432) +FUNCTION_ENTRY_POINT(433) +FUNCTION_ENTRY_POINT(434) +FUNCTION_ENTRY_POINT(435) +FUNCTION_ENTRY_POINT(436) +FUNCTION_ENTRY_POINT(437) +FUNCTION_ENTRY_POINT(438) +FUNCTION_ENTRY_POINT(439) +FUNCTION_ENTRY_POINT(440) +FUNCTION_ENTRY_POINT(441) +FUNCTION_ENTRY_POINT(442) +FUNCTION_ENTRY_POINT(443) +FUNCTION_ENTRY_POINT(444) +FUNCTION_ENTRY_POINT(445) +FUNCTION_ENTRY_POINT(446) +FUNCTION_ENTRY_POINT(447) +FUNCTION_ENTRY_POINT(448) +FUNCTION_ENTRY_POINT(449) +FUNCTION_ENTRY_POINT(450) +FUNCTION_ENTRY_POINT(451) +FUNCTION_ENTRY_POINT(452) +FUNCTION_ENTRY_POINT(453) +FUNCTION_ENTRY_POINT(454) +FUNCTION_ENTRY_POINT(455) +FUNCTION_ENTRY_POINT(456) +FUNCTION_ENTRY_POINT(457) +FUNCTION_ENTRY_POINT(458) +FUNCTION_ENTRY_POINT(459) +FUNCTION_ENTRY_POINT(460) +FUNCTION_ENTRY_POINT(461) +FUNCTION_ENTRY_POINT(462) +FUNCTION_ENTRY_POINT(463) +FUNCTION_ENTRY_POINT(464) +FUNCTION_ENTRY_POINT(465) +FUNCTION_ENTRY_POINT(466) +FUNCTION_ENTRY_POINT(467) +FUNCTION_ENTRY_POINT(468) +FUNCTION_ENTRY_POINT(469) +FUNCTION_ENTRY_POINT(470) +FUNCTION_ENTRY_POINT(471) +FUNCTION_ENTRY_POINT(472) +FUNCTION_ENTRY_POINT(473) +FUNCTION_ENTRY_POINT(474) +FUNCTION_ENTRY_POINT(475) +FUNCTION_ENTRY_POINT(476) +FUNCTION_ENTRY_POINT(477) +FUNCTION_ENTRY_POINT(478) +FUNCTION_ENTRY_POINT(479) +FUNCTION_ENTRY_POINT(480) +FUNCTION_ENTRY_POINT(481) +FUNCTION_ENTRY_POINT(482) +FUNCTION_ENTRY_POINT(483) +FUNCTION_ENTRY_POINT(484) +FUNCTION_ENTRY_POINT(485) +FUNCTION_ENTRY_POINT(486) +FUNCTION_ENTRY_POINT(487) +FUNCTION_ENTRY_POINT(488) +FUNCTION_ENTRY_POINT(489) +FUNCTION_ENTRY_POINT(490) +FUNCTION_ENTRY_POINT(491) +FUNCTION_ENTRY_POINT(492) +FUNCTION_ENTRY_POINT(493) +FUNCTION_ENTRY_POINT(494) +FUNCTION_ENTRY_POINT(495) +FUNCTION_ENTRY_POINT(496) +FUNCTION_ENTRY_POINT(497) +FUNCTION_ENTRY_POINT(498) +FUNCTION_ENTRY_POINT(499) +FUNCTION_ENTRY_POINT(500) +FUNCTION_ENTRY_POINT(501) +FUNCTION_ENTRY_POINT(502) +FUNCTION_ENTRY_POINT(503) +FUNCTION_ENTRY_POINT(504) +FUNCTION_ENTRY_POINT(505) +FUNCTION_ENTRY_POINT(506) +FUNCTION_ENTRY_POINT(507) +FUNCTION_ENTRY_POINT(508) +FUNCTION_ENTRY_POINT(509) +FUNCTION_ENTRY_POINT(510) +FUNCTION_ENTRY_POINT(511) +FUNCTION_ENTRY_POINT(512) +FUNCTION_ENTRY_POINT(513) +FUNCTION_ENTRY_POINT(514) +FUNCTION_ENTRY_POINT(515) +FUNCTION_ENTRY_POINT(516) +FUNCTION_ENTRY_POINT(517) +FUNCTION_ENTRY_POINT(518) +FUNCTION_ENTRY_POINT(519) +FUNCTION_ENTRY_POINT(520) +FUNCTION_ENTRY_POINT(521) +FUNCTION_ENTRY_POINT(522) +FUNCTION_ENTRY_POINT(523) +FUNCTION_ENTRY_POINT(524) +FUNCTION_ENTRY_POINT(525) +FUNCTION_ENTRY_POINT(526) +FUNCTION_ENTRY_POINT(527) +FUNCTION_ENTRY_POINT(528) +FUNCTION_ENTRY_POINT(529) +FUNCTION_ENTRY_POINT(530) +FUNCTION_ENTRY_POINT(531) +FUNCTION_ENTRY_POINT(532) +FUNCTION_ENTRY_POINT(533) +FUNCTION_ENTRY_POINT(534) +FUNCTION_ENTRY_POINT(535) +FUNCTION_ENTRY_POINT(536) +FUNCTION_ENTRY_POINT(537) +FUNCTION_ENTRY_POINT(538) +FUNCTION_ENTRY_POINT(539) +FUNCTION_ENTRY_POINT(540) +FUNCTION_ENTRY_POINT(541) +FUNCTION_ENTRY_POINT(542) +FUNCTION_ENTRY_POINT(543) +FUNCTION_ENTRY_POINT(544) +FUNCTION_ENTRY_POINT(545) +FUNCTION_ENTRY_POINT(546) +FUNCTION_ENTRY_POINT(547) +FUNCTION_ENTRY_POINT(548) +FUNCTION_ENTRY_POINT(549) +FUNCTION_ENTRY_POINT(550) +FUNCTION_ENTRY_POINT(551) +FUNCTION_ENTRY_POINT(552) +FUNCTION_ENTRY_POINT(553) +FUNCTION_ENTRY_POINT(554) +FUNCTION_ENTRY_POINT(555) +FUNCTION_ENTRY_POINT(556) +FUNCTION_ENTRY_POINT(557) +FUNCTION_ENTRY_POINT(558) +FUNCTION_ENTRY_POINT(559) +FUNCTION_ENTRY_POINT(560) +FUNCTION_ENTRY_POINT(561) +FUNCTION_ENTRY_POINT(562) +FUNCTION_ENTRY_POINT(563) +FUNCTION_ENTRY_POINT(564) +FUNCTION_ENTRY_POINT(565) +FUNCTION_ENTRY_POINT(566) +FUNCTION_ENTRY_POINT(567) +FUNCTION_ENTRY_POINT(568) +FUNCTION_ENTRY_POINT(569) +FUNCTION_ENTRY_POINT(570) +FUNCTION_ENTRY_POINT(571) +FUNCTION_ENTRY_POINT(572) +FUNCTION_ENTRY_POINT(573) +FUNCTION_ENTRY_POINT(574) +FUNCTION_ENTRY_POINT(575) +FUNCTION_ENTRY_POINT(576) +FUNCTION_ENTRY_POINT(577) +FUNCTION_ENTRY_POINT(578) +FUNCTION_ENTRY_POINT(579) +FUNCTION_ENTRY_POINT(580) +FUNCTION_ENTRY_POINT(581) +FUNCTION_ENTRY_POINT(582) +FUNCTION_ENTRY_POINT(583) +FUNCTION_ENTRY_POINT(584) +FUNCTION_ENTRY_POINT(585) +FUNCTION_ENTRY_POINT(586) +FUNCTION_ENTRY_POINT(587) +FUNCTION_ENTRY_POINT(588) +FUNCTION_ENTRY_POINT(589) +FUNCTION_ENTRY_POINT(590) +FUNCTION_ENTRY_POINT(591) +FUNCTION_ENTRY_POINT(592) +FUNCTION_ENTRY_POINT(593) +FUNCTION_ENTRY_POINT(594) +FUNCTION_ENTRY_POINT(595) +FUNCTION_ENTRY_POINT(596) +FUNCTION_ENTRY_POINT(597) +FUNCTION_ENTRY_POINT(598) +FUNCTION_ENTRY_POINT(599) +FUNCTION_ENTRY_POINT(600) +FUNCTION_ENTRY_POINT(601) +FUNCTION_ENTRY_POINT(602) +FUNCTION_ENTRY_POINT(603) +FUNCTION_ENTRY_POINT(604) +FUNCTION_ENTRY_POINT(605) +FUNCTION_ENTRY_POINT(606) +FUNCTION_ENTRY_POINT(607) +FUNCTION_ENTRY_POINT(608) +FUNCTION_ENTRY_POINT(609) +FUNCTION_ENTRY_POINT(610) +FUNCTION_ENTRY_POINT(611) +FUNCTION_ENTRY_POINT(612) +FUNCTION_ENTRY_POINT(613) +FUNCTION_ENTRY_POINT(614) +FUNCTION_ENTRY_POINT(615) +FUNCTION_ENTRY_POINT(616) +FUNCTION_ENTRY_POINT(617) +FUNCTION_ENTRY_POINT(618) +FUNCTION_ENTRY_POINT(619) +FUNCTION_ENTRY_POINT(620) +FUNCTION_ENTRY_POINT(621) +FUNCTION_ENTRY_POINT(622) +FUNCTION_ENTRY_POINT(623) +FUNCTION_ENTRY_POINT(624) +FUNCTION_ENTRY_POINT(625) +FUNCTION_ENTRY_POINT(626) +FUNCTION_ENTRY_POINT(627) +FUNCTION_ENTRY_POINT(628) +FUNCTION_ENTRY_POINT(629) +FUNCTION_ENTRY_POINT(630) +FUNCTION_ENTRY_POINT(631) +FUNCTION_ENTRY_POINT(632) +FUNCTION_ENTRY_POINT(633) +FUNCTION_ENTRY_POINT(634) +FUNCTION_ENTRY_POINT(635) +FUNCTION_ENTRY_POINT(636) +FUNCTION_ENTRY_POINT(637) +FUNCTION_ENTRY_POINT(638) +FUNCTION_ENTRY_POINT(639) +FUNCTION_ENTRY_POINT(640) +FUNCTION_ENTRY_POINT(641) +FUNCTION_ENTRY_POINT(642) +FUNCTION_ENTRY_POINT(643) +FUNCTION_ENTRY_POINT(644) +FUNCTION_ENTRY_POINT(645) +FUNCTION_ENTRY_POINT(646) +FUNCTION_ENTRY_POINT(647) +FUNCTION_ENTRY_POINT(648) +FUNCTION_ENTRY_POINT(649) +FUNCTION_ENTRY_POINT(650) +FUNCTION_ENTRY_POINT(651) +FUNCTION_ENTRY_POINT(652) +FUNCTION_ENTRY_POINT(653) +FUNCTION_ENTRY_POINT(654) +FUNCTION_ENTRY_POINT(655) +FUNCTION_ENTRY_POINT(656) +FUNCTION_ENTRY_POINT(657) +FUNCTION_ENTRY_POINT(658) +FUNCTION_ENTRY_POINT(659) +FUNCTION_ENTRY_POINT(660) +FUNCTION_ENTRY_POINT(661) +FUNCTION_ENTRY_POINT(662) +FUNCTION_ENTRY_POINT(663) +FUNCTION_ENTRY_POINT(664) +FUNCTION_ENTRY_POINT(665) +FUNCTION_ENTRY_POINT(666) +FUNCTION_ENTRY_POINT(667) +FUNCTION_ENTRY_POINT(668) +FUNCTION_ENTRY_POINT(669) +FUNCTION_ENTRY_POINT(670) +FUNCTION_ENTRY_POINT(671) +FUNCTION_ENTRY_POINT(672) +FUNCTION_ENTRY_POINT(673) +FUNCTION_ENTRY_POINT(674) +FUNCTION_ENTRY_POINT(675) +FUNCTION_ENTRY_POINT(676) +FUNCTION_ENTRY_POINT(677) +FUNCTION_ENTRY_POINT(678) +FUNCTION_ENTRY_POINT(679) +FUNCTION_ENTRY_POINT(680) +FUNCTION_ENTRY_POINT(681) +FUNCTION_ENTRY_POINT(682) +FUNCTION_ENTRY_POINT(683) +FUNCTION_ENTRY_POINT(684) +FUNCTION_ENTRY_POINT(685) +FUNCTION_ENTRY_POINT(686) +FUNCTION_ENTRY_POINT(687) +FUNCTION_ENTRY_POINT(688) +FUNCTION_ENTRY_POINT(689) +FUNCTION_ENTRY_POINT(690) +FUNCTION_ENTRY_POINT(691) +FUNCTION_ENTRY_POINT(692) +FUNCTION_ENTRY_POINT(693) +FUNCTION_ENTRY_POINT(694) +FUNCTION_ENTRY_POINT(695) +FUNCTION_ENTRY_POINT(696) +FUNCTION_ENTRY_POINT(697) +FUNCTION_ENTRY_POINT(698) +FUNCTION_ENTRY_POINT(699) +FUNCTION_ENTRY_POINT(700) +FUNCTION_ENTRY_POINT(701) +FUNCTION_ENTRY_POINT(702) +FUNCTION_ENTRY_POINT(703) +FUNCTION_ENTRY_POINT(704) +FUNCTION_ENTRY_POINT(705) +FUNCTION_ENTRY_POINT(706) +FUNCTION_ENTRY_POINT(707) +FUNCTION_ENTRY_POINT(708) +FUNCTION_ENTRY_POINT(709) +FUNCTION_ENTRY_POINT(710) +FUNCTION_ENTRY_POINT(711) +FUNCTION_ENTRY_POINT(712) +FUNCTION_ENTRY_POINT(713) +FUNCTION_ENTRY_POINT(714) +FUNCTION_ENTRY_POINT(715) +FUNCTION_ENTRY_POINT(716) +FUNCTION_ENTRY_POINT(717) +FUNCTION_ENTRY_POINT(718) +FUNCTION_ENTRY_POINT(719) +FUNCTION_ENTRY_POINT(720) +FUNCTION_ENTRY_POINT(721) +FUNCTION_ENTRY_POINT(722) +FUNCTION_ENTRY_POINT(723) +FUNCTION_ENTRY_POINT(724) +FUNCTION_ENTRY_POINT(725) +FUNCTION_ENTRY_POINT(726) +FUNCTION_ENTRY_POINT(727) +FUNCTION_ENTRY_POINT(728) +FUNCTION_ENTRY_POINT(729) +FUNCTION_ENTRY_POINT(730) +FUNCTION_ENTRY_POINT(731) +FUNCTION_ENTRY_POINT(732) +FUNCTION_ENTRY_POINT(733) +FUNCTION_ENTRY_POINT(734) +FUNCTION_ENTRY_POINT(735) +FUNCTION_ENTRY_POINT(736) +FUNCTION_ENTRY_POINT(737) +FUNCTION_ENTRY_POINT(738) +FUNCTION_ENTRY_POINT(739) +FUNCTION_ENTRY_POINT(740) +FUNCTION_ENTRY_POINT(741) +FUNCTION_ENTRY_POINT(742) +FUNCTION_ENTRY_POINT(743) +FUNCTION_ENTRY_POINT(744) +FUNCTION_ENTRY_POINT(745) +FUNCTION_ENTRY_POINT(746) +FUNCTION_ENTRY_POINT(747) +FUNCTION_ENTRY_POINT(748) +FUNCTION_ENTRY_POINT(749) +FUNCTION_ENTRY_POINT(750) +FUNCTION_ENTRY_POINT(751) +FUNCTION_ENTRY_POINT(752) +FUNCTION_ENTRY_POINT(753) +FUNCTION_ENTRY_POINT(754) +FUNCTION_ENTRY_POINT(755) +FUNCTION_ENTRY_POINT(756) +FUNCTION_ENTRY_POINT(757) +FUNCTION_ENTRY_POINT(758) +FUNCTION_ENTRY_POINT(759) +FUNCTION_ENTRY_POINT(760) +FUNCTION_ENTRY_POINT(761) +FUNCTION_ENTRY_POINT(762) +FUNCTION_ENTRY_POINT(763) +FUNCTION_ENTRY_POINT(764) +FUNCTION_ENTRY_POINT(765) +FUNCTION_ENTRY_POINT(766) +FUNCTION_ENTRY_POINT(767) +FUNCTION_ENTRY_POINT(768) +FUNCTION_ENTRY_POINT(769) +FUNCTION_ENTRY_POINT(770) +FUNCTION_ENTRY_POINT(771) +FUNCTION_ENTRY_POINT(772) +FUNCTION_ENTRY_POINT(773) +FUNCTION_ENTRY_POINT(774) +FUNCTION_ENTRY_POINT(775) +FUNCTION_ENTRY_POINT(776) +FUNCTION_ENTRY_POINT(777) +FUNCTION_ENTRY_POINT(778) +FUNCTION_ENTRY_POINT(779) +FUNCTION_ENTRY_POINT(780) +FUNCTION_ENTRY_POINT(781) +FUNCTION_ENTRY_POINT(782) +FUNCTION_ENTRY_POINT(783) +FUNCTION_ENTRY_POINT(784) +FUNCTION_ENTRY_POINT(785) +FUNCTION_ENTRY_POINT(786) +FUNCTION_ENTRY_POINT(787) +FUNCTION_ENTRY_POINT(788) +FUNCTION_ENTRY_POINT(789) +FUNCTION_ENTRY_POINT(790) +FUNCTION_ENTRY_POINT(791) +FUNCTION_ENTRY_POINT(792) +FUNCTION_ENTRY_POINT(793) +FUNCTION_ENTRY_POINT(794) +FUNCTION_ENTRY_POINT(795) +FUNCTION_ENTRY_POINT(796) +FUNCTION_ENTRY_POINT(797) +FUNCTION_ENTRY_POINT(798) +FUNCTION_ENTRY_POINT(799) +FUNCTION_ENTRY_POINT(800) +FUNCTION_ENTRY_POINT(801) +FUNCTION_ENTRY_POINT(802) +FUNCTION_ENTRY_POINT(803) +FUNCTION_ENTRY_POINT(804) +FUNCTION_ENTRY_POINT(805) +FUNCTION_ENTRY_POINT(806) +FUNCTION_ENTRY_POINT(807) +FUNCTION_ENTRY_POINT(808) +FUNCTION_ENTRY_POINT(809) +FUNCTION_ENTRY_POINT(810) +FUNCTION_ENTRY_POINT(811) +FUNCTION_ENTRY_POINT(812) +FUNCTION_ENTRY_POINT(813) +FUNCTION_ENTRY_POINT(814) +FUNCTION_ENTRY_POINT(815) +FUNCTION_ENTRY_POINT(816) +FUNCTION_ENTRY_POINT(817) +FUNCTION_ENTRY_POINT(818) +FUNCTION_ENTRY_POINT(819) +FUNCTION_ENTRY_POINT(820) +FUNCTION_ENTRY_POINT(821) +FUNCTION_ENTRY_POINT(822) +FUNCTION_ENTRY_POINT(823) +FUNCTION_ENTRY_POINT(824) +FUNCTION_ENTRY_POINT(825) +FUNCTION_ENTRY_POINT(826) +FUNCTION_ENTRY_POINT(827) +FUNCTION_ENTRY_POINT(828) +FUNCTION_ENTRY_POINT(829) +FUNCTION_ENTRY_POINT(830) +FUNCTION_ENTRY_POINT(831) +FUNCTION_ENTRY_POINT(832) +FUNCTION_ENTRY_POINT(833) +FUNCTION_ENTRY_POINT(834) +FUNCTION_ENTRY_POINT(835) +FUNCTION_ENTRY_POINT(836) +FUNCTION_ENTRY_POINT(837) +FUNCTION_ENTRY_POINT(838) +FUNCTION_ENTRY_POINT(839) +FUNCTION_ENTRY_POINT(840) +FUNCTION_ENTRY_POINT(841) +FUNCTION_ENTRY_POINT(842) +FUNCTION_ENTRY_POINT(843) +FUNCTION_ENTRY_POINT(844) +FUNCTION_ENTRY_POINT(845) +FUNCTION_ENTRY_POINT(846) +FUNCTION_ENTRY_POINT(847) +FUNCTION_ENTRY_POINT(848) +FUNCTION_ENTRY_POINT(849) +FUNCTION_ENTRY_POINT(850) +FUNCTION_ENTRY_POINT(851) +FUNCTION_ENTRY_POINT(852) +FUNCTION_ENTRY_POINT(853) +FUNCTION_ENTRY_POINT(854) +FUNCTION_ENTRY_POINT(855) +FUNCTION_ENTRY_POINT(856) +FUNCTION_ENTRY_POINT(857) +FUNCTION_ENTRY_POINT(858) +FUNCTION_ENTRY_POINT(859) +FUNCTION_ENTRY_POINT(860) +FUNCTION_ENTRY_POINT(861) +FUNCTION_ENTRY_POINT(862) +FUNCTION_ENTRY_POINT(863) +FUNCTION_ENTRY_POINT(864) +FUNCTION_ENTRY_POINT(865) +FUNCTION_ENTRY_POINT(866) +FUNCTION_ENTRY_POINT(867) +FUNCTION_ENTRY_POINT(868) +FUNCTION_ENTRY_POINT(869) +FUNCTION_ENTRY_POINT(870) +FUNCTION_ENTRY_POINT(871) +FUNCTION_ENTRY_POINT(872) +FUNCTION_ENTRY_POINT(873) +FUNCTION_ENTRY_POINT(874) +FUNCTION_ENTRY_POINT(875) +FUNCTION_ENTRY_POINT(876) +FUNCTION_ENTRY_POINT(877) +FUNCTION_ENTRY_POINT(878) +FUNCTION_ENTRY_POINT(879) +FUNCTION_ENTRY_POINT(880) +FUNCTION_ENTRY_POINT(881) +FUNCTION_ENTRY_POINT(882) +FUNCTION_ENTRY_POINT(883) +FUNCTION_ENTRY_POINT(884) +FUNCTION_ENTRY_POINT(885) +FUNCTION_ENTRY_POINT(886) +FUNCTION_ENTRY_POINT(887) +FUNCTION_ENTRY_POINT(888) +FUNCTION_ENTRY_POINT(889) +FUNCTION_ENTRY_POINT(890) +FUNCTION_ENTRY_POINT(891) +FUNCTION_ENTRY_POINT(892) +FUNCTION_ENTRY_POINT(893) +FUNCTION_ENTRY_POINT(894) +FUNCTION_ENTRY_POINT(895) +FUNCTION_ENTRY_POINT(896) +FUNCTION_ENTRY_POINT(897) +FUNCTION_ENTRY_POINT(898) +FUNCTION_ENTRY_POINT(899) +FUNCTION_ENTRY_POINT(900) +FUNCTION_ENTRY_POINT(901) +FUNCTION_ENTRY_POINT(902) +FUNCTION_ENTRY_POINT(903) +FUNCTION_ENTRY_POINT(904) +FUNCTION_ENTRY_POINT(905) +FUNCTION_ENTRY_POINT(906) +FUNCTION_ENTRY_POINT(907) +FUNCTION_ENTRY_POINT(908) +FUNCTION_ENTRY_POINT(909) +FUNCTION_ENTRY_POINT(910) +FUNCTION_ENTRY_POINT(911) +FUNCTION_ENTRY_POINT(912) +FUNCTION_ENTRY_POINT(913) +FUNCTION_ENTRY_POINT(914) +FUNCTION_ENTRY_POINT(915) +FUNCTION_ENTRY_POINT(916) +FUNCTION_ENTRY_POINT(917) +FUNCTION_ENTRY_POINT(918) +FUNCTION_ENTRY_POINT(919) +FUNCTION_ENTRY_POINT(920) +FUNCTION_ENTRY_POINT(921) +FUNCTION_ENTRY_POINT(922) +FUNCTION_ENTRY_POINT(923) +FUNCTION_ENTRY_POINT(924) +FUNCTION_ENTRY_POINT(925) +FUNCTION_ENTRY_POINT(926) +FUNCTION_ENTRY_POINT(927) +FUNCTION_ENTRY_POINT(928) +FUNCTION_ENTRY_POINT(929) +FUNCTION_ENTRY_POINT(930) +FUNCTION_ENTRY_POINT(931) +FUNCTION_ENTRY_POINT(932) +FUNCTION_ENTRY_POINT(933) +FUNCTION_ENTRY_POINT(934) +FUNCTION_ENTRY_POINT(935) +FUNCTION_ENTRY_POINT(936) +FUNCTION_ENTRY_POINT(937) +FUNCTION_ENTRY_POINT(938) +FUNCTION_ENTRY_POINT(939) +FUNCTION_ENTRY_POINT(940) +FUNCTION_ENTRY_POINT(941) +FUNCTION_ENTRY_POINT(942) +FUNCTION_ENTRY_POINT(943) +FUNCTION_ENTRY_POINT(944) +FUNCTION_ENTRY_POINT(945) +FUNCTION_ENTRY_POINT(946) +FUNCTION_ENTRY_POINT(947) +FUNCTION_ENTRY_POINT(948) +FUNCTION_ENTRY_POINT(949) +FUNCTION_ENTRY_POINT(950) +FUNCTION_ENTRY_POINT(951) +FUNCTION_ENTRY_POINT(952) +FUNCTION_ENTRY_POINT(953) +FUNCTION_ENTRY_POINT(954) +FUNCTION_ENTRY_POINT(955) +FUNCTION_ENTRY_POINT(956) +FUNCTION_ENTRY_POINT(957) +FUNCTION_ENTRY_POINT(958) +FUNCTION_ENTRY_POINT(959) +FUNCTION_ENTRY_POINT(960) +FUNCTION_ENTRY_POINT(961) +FUNCTION_ENTRY_POINT(962) +FUNCTION_ENTRY_POINT(963) +FUNCTION_ENTRY_POINT(964) +FUNCTION_ENTRY_POINT(965) +FUNCTION_ENTRY_POINT(966) +FUNCTION_ENTRY_POINT(967) +FUNCTION_ENTRY_POINT(968) +FUNCTION_ENTRY_POINT(969) +FUNCTION_ENTRY_POINT(970) +FUNCTION_ENTRY_POINT(971) +FUNCTION_ENTRY_POINT(972) +FUNCTION_ENTRY_POINT(973) +FUNCTION_ENTRY_POINT(974) +FUNCTION_ENTRY_POINT(975) +FUNCTION_ENTRY_POINT(976) +FUNCTION_ENTRY_POINT(977) +FUNCTION_ENTRY_POINT(978) +FUNCTION_ENTRY_POINT(979) +FUNCTION_ENTRY_POINT(980) +FUNCTION_ENTRY_POINT(981) +FUNCTION_ENTRY_POINT(982) +FUNCTION_ENTRY_POINT(983) +FUNCTION_ENTRY_POINT(984) +FUNCTION_ENTRY_POINT(985) +FUNCTION_ENTRY_POINT(986) +FUNCTION_ENTRY_POINT(987) +FUNCTION_ENTRY_POINT(988) +FUNCTION_ENTRY_POINT(989) +FUNCTION_ENTRY_POINT(990) +FUNCTION_ENTRY_POINT(991) +FUNCTION_ENTRY_POINT(992) +FUNCTION_ENTRY_POINT(993) +FUNCTION_ENTRY_POINT(994) +FUNCTION_ENTRY_POINT(995) +FUNCTION_ENTRY_POINT(996) +FUNCTION_ENTRY_POINT(997) +FUNCTION_ENTRY_POINT(998) +FUNCTION_ENTRY_POINT(999) +FUNCTION_ENTRY_POINT(1000) +FUNCTION_ENTRY_POINT(1001) +FUNCTION_ENTRY_POINT(1002) +FUNCTION_ENTRY_POINT(1003) +FUNCTION_ENTRY_POINT(1004) +FUNCTION_ENTRY_POINT(1005) +FUNCTION_ENTRY_POINT(1006) +FUNCTION_ENTRY_POINT(1007) +FUNCTION_ENTRY_POINT(1008) +FUNCTION_ENTRY_POINT(1009) +FUNCTION_ENTRY_POINT(1010) +FUNCTION_ENTRY_POINT(1011) +FUNCTION_ENTRY_POINT(1012) +FUNCTION_ENTRY_POINT(1013) +FUNCTION_ENTRY_POINT(1014) +FUNCTION_ENTRY_POINT(1015) +FUNCTION_ENTRY_POINT(1016) +FUNCTION_ENTRY_POINT(1017) +FUNCTION_ENTRY_POINT(1018) +FUNCTION_ENTRY_POINT(1019) +FUNCTION_ENTRY_POINT(1020) +FUNCTION_ENTRY_POINT(1021) +FUNCTION_ENTRY_POINT(1022) +FUNCTION_ENTRY_POINT(1023) + +const void *InterfaceAdapter::unknownVtbl[] = { + InterfaceAdapter::QueryInterface, + InterfaceAdapter::AddRef, + InterfaceAdapter::Release, + function_3, + function_4, + function_5, + function_6, + function_7, + function_8, + function_9, + function_10, + function_11, + function_12, + function_13, + function_14, + function_15, + function_16, + function_17, + function_18, + function_19, + function_20, + function_21, + function_22, + function_23, + function_24, + function_25, + function_26, + function_27, + function_28, + function_29, + function_30, + function_31, + function_32, + function_33, + function_34, + function_35, + function_36, + function_37, + function_38, + function_39, + function_40, + function_41, + function_42, + function_43, + function_44, + function_45, + function_46, + function_47, + function_48, + function_49, + function_50, + function_51, + function_52, + function_53, + function_54, + function_55, + function_56, + function_57, + function_58, + function_59, + function_60, + function_61, + function_62, + function_63, + function_64, + function_65, + function_66, + function_67, + function_68, + function_69, + function_70, + function_71, + function_72, + function_73, + function_74, + function_75, + function_76, + function_77, + function_78, + function_79, + function_80, + function_81, + function_82, + function_83, + function_84, + function_85, + function_86, + function_87, + function_88, + function_89, + function_90, + function_91, + function_92, + function_93, + function_94, + function_95, + function_96, + function_97, + function_98, + function_99, + function_100, + function_101, + function_102, + function_103, + function_104, + function_105, + function_106, + function_107, + function_108, + function_109, + function_110, + function_111, + function_112, + function_113, + function_114, + function_115, + function_116, + function_117, + function_118, + function_119, + function_120, + function_121, + function_122, + function_123, + function_124, + function_125, + function_126, + function_127, + function_128, + function_129, + function_130, + function_131, + function_132, + function_133, + function_134, + function_135, + function_136, + function_137, + function_138, + function_139, + function_140, + function_141, + function_142, + function_143, + function_144, + function_145, + function_146, + function_147, + function_148, + function_149, + function_150, + function_151, + function_152, + function_153, + function_154, + function_155, + function_156, + function_157, + function_158, + function_159, + function_160, + function_161, + function_162, + function_163, + function_164, + function_165, + function_166, + function_167, + function_168, + function_169, + function_170, + function_171, + function_172, + function_173, + function_174, + function_175, + function_176, + function_177, + function_178, + function_179, + function_180, + function_181, + function_182, + function_183, + function_184, + function_185, + function_186, + function_187, + function_188, + function_189, + function_190, + function_191, + function_192, + function_193, + function_194, + function_195, + function_196, + function_197, + function_198, + function_199, + function_200, + function_201, + function_202, + function_203, + function_204, + function_205, + function_206, + function_207, + function_208, + function_209, + function_210, + function_211, + function_212, + function_213, + function_214, + function_215, + function_216, + function_217, + function_218, + function_219, + function_220, + function_221, + function_222, + function_223, + function_224, + function_225, + function_226, + function_227, + function_228, + function_229, + function_230, + function_231, + function_232, + function_233, + function_234, + function_235, + function_236, + function_237, + function_238, + function_239, + function_240, + function_241, + function_242, + function_243, + function_244, + function_245, + function_246, + function_247, + function_248, + function_249, + function_250, + function_251, + function_252, + function_253, + function_254, + function_255, + function_256, + function_257, + function_258, + function_259, + function_260, + function_261, + function_262, + function_263, + function_264, + function_265, + function_266, + function_267, + function_268, + function_269, + function_270, + function_271, + function_272, + function_273, + function_274, + function_275, + function_276, + function_277, + function_278, + function_279, + function_280, + function_281, + function_282, + function_283, + function_284, + function_285, + function_286, + function_287, + function_288, + function_289, + function_290, + function_291, + function_292, + function_293, + function_294, + function_295, + function_296, + function_297, + function_298, + function_299, + function_300, + function_301, + function_302, + function_303, + function_304, + function_305, + function_306, + function_307, + function_308, + function_309, + function_310, + function_311, + function_312, + function_313, + function_314, + function_315, + function_316, + function_317, + function_318, + function_319, + function_320, + function_321, + function_322, + function_323, + function_324, + function_325, + function_326, + function_327, + function_328, + function_329, + function_330, + function_331, + function_332, + function_333, + function_334, + function_335, + function_336, + function_337, + function_338, + function_339, + function_340, + function_341, + function_342, + function_343, + function_344, + function_345, + function_346, + function_347, + function_348, + function_349, + function_350, + function_351, + function_352, + function_353, + function_354, + function_355, + function_356, + function_357, + function_358, + function_359, + function_360, + function_361, + function_362, + function_363, + function_364, + function_365, + function_366, + function_367, + function_368, + function_369, + function_370, + function_371, + function_372, + function_373, + function_374, + function_375, + function_376, + function_377, + function_378, + function_379, + function_380, + function_381, + function_382, + function_383, + function_384, + function_385, + function_386, + function_387, + function_388, + function_389, + function_390, + function_391, + function_392, + function_393, + function_394, + function_395, + function_396, + function_397, + function_398, + function_399, + function_400, + function_401, + function_402, + function_403, + function_404, + function_405, + function_406, + function_407, + function_408, + function_409, + function_410, + function_411, + function_412, + function_413, + function_414, + function_415, + function_416, + function_417, + function_418, + function_419, + function_420, + function_421, + function_422, + function_423, + function_424, + function_425, + function_426, + function_427, + function_428, + function_429, + function_430, + function_431, + function_432, + function_433, + function_434, + function_435, + function_436, + function_437, + function_438, + function_439, + function_440, + function_441, + function_442, + function_443, + function_444, + function_445, + function_446, + function_447, + function_448, + function_449, + function_450, + function_451, + function_452, + function_453, + function_454, + function_455, + function_456, + function_457, + function_458, + function_459, + function_460, + function_461, + function_462, + function_463, + function_464, + function_465, + function_466, + function_467, + function_468, + function_469, + function_470, + function_471, + function_472, + function_473, + function_474, + function_475, + function_476, + function_477, + function_478, + function_479, + function_480, + function_481, + function_482, + function_483, + function_484, + function_485, + function_486, + function_487, + function_488, + function_489, + function_490, + function_491, + function_492, + function_493, + function_494, + function_495, + function_496, + function_497, + function_498, + function_499, + function_500, + function_501, + function_502, + function_503, + function_504, + function_505, + function_506, + function_507, + function_508, + function_509, + function_510, + function_511, + function_512, + function_513, + function_514, + function_515, + function_516, + function_517, + function_518, + function_519, + function_520, + function_521, + function_522, + function_523, + function_524, + function_525, + function_526, + function_527, + function_528, + function_529, + function_530, + function_531, + function_532, + function_533, + function_534, + function_535, + function_536, + function_537, + function_538, + function_539, + function_540, + function_541, + function_542, + function_543, + function_544, + function_545, + function_546, + function_547, + function_548, + function_549, + function_550, + function_551, + function_552, + function_553, + function_554, + function_555, + function_556, + function_557, + function_558, + function_559, + function_560, + function_561, + function_562, + function_563, + function_564, + function_565, + function_566, + function_567, + function_568, + function_569, + function_570, + function_571, + function_572, + function_573, + function_574, + function_575, + function_576, + function_577, + function_578, + function_579, + function_580, + function_581, + function_582, + function_583, + function_584, + function_585, + function_586, + function_587, + function_588, + function_589, + function_590, + function_591, + function_592, + function_593, + function_594, + function_595, + function_596, + function_597, + function_598, + function_599, + function_600, + function_601, + function_602, + function_603, + function_604, + function_605, + function_606, + function_607, + function_608, + function_609, + function_610, + function_611, + function_612, + function_613, + function_614, + function_615, + function_616, + function_617, + function_618, + function_619, + function_620, + function_621, + function_622, + function_623, + function_624, + function_625, + function_626, + function_627, + function_628, + function_629, + function_630, + function_631, + function_632, + function_633, + function_634, + function_635, + function_636, + function_637, + function_638, + function_639, + function_640, + function_641, + function_642, + function_643, + function_644, + function_645, + function_646, + function_647, + function_648, + function_649, + function_650, + function_651, + function_652, + function_653, + function_654, + function_655, + function_656, + function_657, + function_658, + function_659, + function_660, + function_661, + function_662, + function_663, + function_664, + function_665, + function_666, + function_667, + function_668, + function_669, + function_670, + function_671, + function_672, + function_673, + function_674, + function_675, + function_676, + function_677, + function_678, + function_679, + function_680, + function_681, + function_682, + function_683, + function_684, + function_685, + function_686, + function_687, + function_688, + function_689, + function_690, + function_691, + function_692, + function_693, + function_694, + function_695, + function_696, + function_697, + function_698, + function_699, + function_700, + function_701, + function_702, + function_703, + function_704, + function_705, + function_706, + function_707, + function_708, + function_709, + function_710, + function_711, + function_712, + function_713, + function_714, + function_715, + function_716, + function_717, + function_718, + function_719, + function_720, + function_721, + function_722, + function_723, + function_724, + function_725, + function_726, + function_727, + function_728, + function_729, + function_730, + function_731, + function_732, + function_733, + function_734, + function_735, + function_736, + function_737, + function_738, + function_739, + function_740, + function_741, + function_742, + function_743, + function_744, + function_745, + function_746, + function_747, + function_748, + function_749, + function_750, + function_751, + function_752, + function_753, + function_754, + function_755, + function_756, + function_757, + function_758, + function_759, + function_760, + function_761, + function_762, + function_763, + function_764, + function_765, + function_766, + function_767, + function_768, + function_769, + function_770, + function_771, + function_772, + function_773, + function_774, + function_775, + function_776, + function_777, + function_778, + function_779, + function_780, + function_781, + function_782, + function_783, + function_784, + function_785, + function_786, + function_787, + function_788, + function_789, + function_790, + function_791, + function_792, + function_793, + function_794, + function_795, + function_796, + function_797, + function_798, + function_799, + function_800, + function_801, + function_802, + function_803, + function_804, + function_805, + function_806, + function_807, + function_808, + function_809, + function_810, + function_811, + function_812, + function_813, + function_814, + function_815, + function_816, + function_817, + function_818, + function_819, + function_820, + function_821, + function_822, + function_823, + function_824, + function_825, + function_826, + function_827, + function_828, + function_829, + function_830, + function_831, + function_832, + function_833, + function_834, + function_835, + function_836, + function_837, + function_838, + function_839, + function_840, + function_841, + function_842, + function_843, + function_844, + function_845, + function_846, + function_847, + function_848, + function_849, + function_850, + function_851, + function_852, + function_853, + function_854, + function_855, + function_856, + function_857, + function_858, + function_859, + function_860, + function_861, + function_862, + function_863, + function_864, + function_865, + function_866, + function_867, + function_868, + function_869, + function_870, + function_871, + function_872, + function_873, + function_874, + function_875, + function_876, + function_877, + function_878, + function_879, + function_880, + function_881, + function_882, + function_883, + function_884, + function_885, + function_886, + function_887, + function_888, + function_889, + function_890, + function_891, + function_892, + function_893, + function_894, + function_895, + function_896, + function_897, + function_898, + function_899, + function_900, + function_901, + function_902, + function_903, + function_904, + function_905, + function_906, + function_907, + function_908, + function_909, + function_910, + function_911, + function_912, + function_913, + function_914, + function_915, + function_916, + function_917, + function_918, + function_919, + function_920, + function_921, + function_922, + function_923, + function_924, + function_925, + function_926, + function_927, + function_928, + function_929, + function_930, + function_931, + function_932, + function_933, + function_934, + function_935, + function_936, + function_937, + function_938, + function_939, + function_940, + function_941, + function_942, + function_943, + function_944, + function_945, + function_946, + function_947, + function_948, + function_949, + function_950, + function_951, + function_952, + function_953, + function_954, + function_955, + function_956, + function_957, + function_958, + function_959, + function_960, + function_961, + function_962, + function_963, + function_964, + function_965, + function_966, + function_967, + function_968, + function_969, + function_970, + function_971, + function_972, + function_973, + function_974, + function_975, + function_976, + function_977, + function_978, + function_979, + function_980, + function_981, + function_982, + function_983, + function_984, + function_985, + function_986, + function_987, + function_988, + function_989, + function_990, + function_991, + function_992, + function_993, + function_994, + function_995, + function_996, + function_997, + function_998, + function_999, + function_1000, + function_1001, + function_1002, + function_1003, + function_1004, + function_1005, + function_1006, + function_1007, + function_1008, + function_1009, + function_1010, + function_1011, + function_1012, + function_1013, + function_1014, + function_1015, + function_1016, + function_1017, + function_1018, + function_1019, + function_1020, + function_1021, + function_1022, + function_1023 +}; + +const void *InterfaceAdapter::dispatchVtbl[] = { + InterfaceAdapter::QueryInterface, + InterfaceAdapter::AddRef, + InterfaceAdapter::Release, + InterfaceAdapter::GetTypeInfoCount, + InterfaceAdapter::GetTypeInfo, + InterfaceAdapter::GetIDsOfNames, + InterfaceAdapter::Invoke, + function_7, + function_8, + function_9, + function_10, + function_11, + function_12, + function_13, + function_14, + function_15, + function_16, + function_17, + function_18, + function_19, + function_20, + function_21, + function_22, + function_23, + function_24, + function_25, + function_26, + function_27, + function_28, + function_29, + function_30, + function_31, + function_32, + function_33, + function_34, + function_35, + function_36, + function_37, + function_38, + function_39, + function_40, + function_41, + function_42, + function_43, + function_44, + function_45, + function_46, + function_47, + function_48, + function_49, + function_50, + function_51, + function_52, + function_53, + function_54, + function_55, + function_56, + function_57, + function_58, + function_59, + function_60, + function_61, + function_62, + function_63, + function_64, + function_65, + function_66, + function_67, + function_68, + function_69, + function_70, + function_71, + function_72, + function_73, + function_74, + function_75, + function_76, + function_77, + function_78, + function_79, + function_80, + function_81, + function_82, + function_83, + function_84, + function_85, + function_86, + function_87, + function_88, + function_89, + function_90, + function_91, + function_92, + function_93, + function_94, + function_95, + function_96, + function_97, + function_98, + function_99, + function_100, + function_101, + function_102, + function_103, + function_104, + function_105, + function_106, + function_107, + function_108, + function_109, + function_110, + function_111, + function_112, + function_113, + function_114, + function_115, + function_116, + function_117, + function_118, + function_119, + function_120, + function_121, + function_122, + function_123, + function_124, + function_125, + function_126, + function_127, + function_128, + function_129, + function_130, + function_131, + function_132, + function_133, + function_134, + function_135, + function_136, + function_137, + function_138, + function_139, + function_140, + function_141, + function_142, + function_143, + function_144, + function_145, + function_146, + function_147, + function_148, + function_149, + function_150, + function_151, + function_152, + function_153, + function_154, + function_155, + function_156, + function_157, + function_158, + function_159, + function_160, + function_161, + function_162, + function_163, + function_164, + function_165, + function_166, + function_167, + function_168, + function_169, + function_170, + function_171, + function_172, + function_173, + function_174, + function_175, + function_176, + function_177, + function_178, + function_179, + function_180, + function_181, + function_182, + function_183, + function_184, + function_185, + function_186, + function_187, + function_188, + function_189, + function_190, + function_191, + function_192, + function_193, + function_194, + function_195, + function_196, + function_197, + function_198, + function_199, + function_200, + function_201, + function_202, + function_203, + function_204, + function_205, + function_206, + function_207, + function_208, + function_209, + function_210, + function_211, + function_212, + function_213, + function_214, + function_215, + function_216, + function_217, + function_218, + function_219, + function_220, + function_221, + function_222, + function_223, + function_224, + function_225, + function_226, + function_227, + function_228, + function_229, + function_230, + function_231, + function_232, + function_233, + function_234, + function_235, + function_236, + function_237, + function_238, + function_239, + function_240, + function_241, + function_242, + function_243, + function_244, + function_245, + function_246, + function_247, + function_248, + function_249, + function_250, + function_251, + function_252, + function_253, + function_254, + function_255, + function_256, + function_257, + function_258, + function_259, + function_260, + function_261, + function_262, + function_263, + function_264, + function_265, + function_266, + function_267, + function_268, + function_269, + function_270, + function_271, + function_272, + function_273, + function_274, + function_275, + function_276, + function_277, + function_278, + function_279, + function_280, + function_281, + function_282, + function_283, + function_284, + function_285, + function_286, + function_287, + function_288, + function_289, + function_290, + function_291, + function_292, + function_293, + function_294, + function_295, + function_296, + function_297, + function_298, + function_299, + function_300, + function_301, + function_302, + function_303, + function_304, + function_305, + function_306, + function_307, + function_308, + function_309, + function_310, + function_311, + function_312, + function_313, + function_314, + function_315, + function_316, + function_317, + function_318, + function_319, + function_320, + function_321, + function_322, + function_323, + function_324, + function_325, + function_326, + function_327, + function_328, + function_329, + function_330, + function_331, + function_332, + function_333, + function_334, + function_335, + function_336, + function_337, + function_338, + function_339, + function_340, + function_341, + function_342, + function_343, + function_344, + function_345, + function_346, + function_347, + function_348, + function_349, + function_350, + function_351, + function_352, + function_353, + function_354, + function_355, + function_356, + function_357, + function_358, + function_359, + function_360, + function_361, + function_362, + function_363, + function_364, + function_365, + function_366, + function_367, + function_368, + function_369, + function_370, + function_371, + function_372, + function_373, + function_374, + function_375, + function_376, + function_377, + function_378, + function_379, + function_380, + function_381, + function_382, + function_383, + function_384, + function_385, + function_386, + function_387, + function_388, + function_389, + function_390, + function_391, + function_392, + function_393, + function_394, + function_395, + function_396, + function_397, + function_398, + function_399, + function_400, + function_401, + function_402, + function_403, + function_404, + function_405, + function_406, + function_407, + function_408, + function_409, + function_410, + function_411, + function_412, + function_413, + function_414, + function_415, + function_416, + function_417, + function_418, + function_419, + function_420, + function_421, + function_422, + function_423, + function_424, + function_425, + function_426, + function_427, + function_428, + function_429, + function_430, + function_431, + function_432, + function_433, + function_434, + function_435, + function_436, + function_437, + function_438, + function_439, + function_440, + function_441, + function_442, + function_443, + function_444, + function_445, + function_446, + function_447, + function_448, + function_449, + function_450, + function_451, + function_452, + function_453, + function_454, + function_455, + function_456, + function_457, + function_458, + function_459, + function_460, + function_461, + function_462, + function_463, + function_464, + function_465, + function_466, + function_467, + function_468, + function_469, + function_470, + function_471, + function_472, + function_473, + function_474, + function_475, + function_476, + function_477, + function_478, + function_479, + function_480, + function_481, + function_482, + function_483, + function_484, + function_485, + function_486, + function_487, + function_488, + function_489, + function_490, + function_491, + function_492, + function_493, + function_494, + function_495, + function_496, + function_497, + function_498, + function_499, + function_500, + function_501, + function_502, + function_503, + function_504, + function_505, + function_506, + function_507, + function_508, + function_509, + function_510, + function_511, + function_512, + function_513, + function_514, + function_515, + function_516, + function_517, + function_518, + function_519, + function_520, + function_521, + function_522, + function_523, + function_524, + function_525, + function_526, + function_527, + function_528, + function_529, + function_530, + function_531, + function_532, + function_533, + function_534, + function_535, + function_536, + function_537, + function_538, + function_539, + function_540, + function_541, + function_542, + function_543, + function_544, + function_545, + function_546, + function_547, + function_548, + function_549, + function_550, + function_551, + function_552, + function_553, + function_554, + function_555, + function_556, + function_557, + function_558, + function_559, + function_560, + function_561, + function_562, + function_563, + function_564, + function_565, + function_566, + function_567, + function_568, + function_569, + function_570, + function_571, + function_572, + function_573, + function_574, + function_575, + function_576, + function_577, + function_578, + function_579, + function_580, + function_581, + function_582, + function_583, + function_584, + function_585, + function_586, + function_587, + function_588, + function_589, + function_590, + function_591, + function_592, + function_593, + function_594, + function_595, + function_596, + function_597, + function_598, + function_599, + function_600, + function_601, + function_602, + function_603, + function_604, + function_605, + function_606, + function_607, + function_608, + function_609, + function_610, + function_611, + function_612, + function_613, + function_614, + function_615, + function_616, + function_617, + function_618, + function_619, + function_620, + function_621, + function_622, + function_623, + function_624, + function_625, + function_626, + function_627, + function_628, + function_629, + function_630, + function_631, + function_632, + function_633, + function_634, + function_635, + function_636, + function_637, + function_638, + function_639, + function_640, + function_641, + function_642, + function_643, + function_644, + function_645, + function_646, + function_647, + function_648, + function_649, + function_650, + function_651, + function_652, + function_653, + function_654, + function_655, + function_656, + function_657, + function_658, + function_659, + function_660, + function_661, + function_662, + function_663, + function_664, + function_665, + function_666, + function_667, + function_668, + function_669, + function_670, + function_671, + function_672, + function_673, + function_674, + function_675, + function_676, + function_677, + function_678, + function_679, + function_680, + function_681, + function_682, + function_683, + function_684, + function_685, + function_686, + function_687, + function_688, + function_689, + function_690, + function_691, + function_692, + function_693, + function_694, + function_695, + function_696, + function_697, + function_698, + function_699, + function_700, + function_701, + function_702, + function_703, + function_704, + function_705, + function_706, + function_707, + function_708, + function_709, + function_710, + function_711, + function_712, + function_713, + function_714, + function_715, + function_716, + function_717, + function_718, + function_719, + function_720, + function_721, + function_722, + function_723, + function_724, + function_725, + function_726, + function_727, + function_728, + function_729, + function_730, + function_731, + function_732, + function_733, + function_734, + function_735, + function_736, + function_737, + function_738, + function_739, + function_740, + function_741, + function_742, + function_743, + function_744, + function_745, + function_746, + function_747, + function_748, + function_749, + function_750, + function_751, + function_752, + function_753, + function_754, + function_755, + function_756, + function_757, + function_758, + function_759, + function_760, + function_761, + function_762, + function_763, + function_764, + function_765, + function_766, + function_767, + function_768, + function_769, + function_770, + function_771, + function_772, + function_773, + function_774, + function_775, + function_776, + function_777, + function_778, + function_779, + function_780, + function_781, + function_782, + function_783, + function_784, + function_785, + function_786, + function_787, + function_788, + function_789, + function_790, + function_791, + function_792, + function_793, + function_794, + function_795, + function_796, + function_797, + function_798, + function_799, + function_800, + function_801, + function_802, + function_803, + function_804, + function_805, + function_806, + function_807, + function_808, + function_809, + function_810, + function_811, + function_812, + function_813, + function_814, + function_815, + function_816, + function_817, + function_818, + function_819, + function_820, + function_821, + function_822, + function_823, + function_824, + function_825, + function_826, + function_827, + function_828, + function_829, + function_830, + function_831, + function_832, + function_833, + function_834, + function_835, + function_836, + function_837, + function_838, + function_839, + function_840, + function_841, + function_842, + function_843, + function_844, + function_845, + function_846, + function_847, + function_848, + function_849, + function_850, + function_851, + function_852, + function_853, + function_854, + function_855, + function_856, + function_857, + function_858, + function_859, + function_860, + function_861, + function_862, + function_863, + function_864, + function_865, + function_866, + function_867, + function_868, + function_869, + function_870, + function_871, + function_872, + function_873, + function_874, + function_875, + function_876, + function_877, + function_878, + function_879, + function_880, + function_881, + function_882, + function_883, + function_884, + function_885, + function_886, + function_887, + function_888, + function_889, + function_890, + function_891, + function_892, + function_893, + function_894, + function_895, + function_896, + function_897, + function_898, + function_899, + function_900, + function_901, + function_902, + function_903, + function_904, + function_905, + function_906, + function_907, + function_908, + function_909, + function_910, + function_911, + function_912, + function_913, + function_914, + function_915, + function_916, + function_917, + function_918, + function_919, + function_920, + function_921, + function_922, + function_923, + function_924, + function_925, + function_926, + function_927, + function_928, + function_929, + function_930, + function_931, + function_932, + function_933, + function_934, + function_935, + function_936, + function_937, + function_938, + function_939, + function_940, + function_941, + function_942, + function_943, + function_944, + function_945, + function_946, + function_947, + function_948, + function_949, + function_950, + function_951, + function_952, + function_953, + function_954, + function_955, + function_956, + function_957, + function_958, + function_959, + function_960, + function_961, + function_962, + function_963, + function_964, + function_965, + function_966, + function_967, + function_968, + function_969, + function_970, + function_971, + function_972, + function_973, + function_974, + function_975, + function_976, + function_977, + function_978, + function_979, + function_980, + function_981, + function_982, + function_983, + function_984, + function_985, + function_986, + function_987, + function_988, + function_989, + function_990, + function_991, + function_992, + function_993, + function_994, + function_995, + function_996, + function_997, + function_998, + function_999, + function_1000, + function_1001, + function_1002, + function_1003, + function_1004, + function_1005, + function_1006, + function_1007, + function_1008, + function_1009, + function_1010, + function_1011, + function_1012, + function_1013, + function_1014, + function_1015, + function_1016, + function_1017, + function_1018, + function_1019, + function_1020, + function_1021, + function_1022, + function_1023 +}; diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..9ce33e0 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,21 @@ +# $Id: Makefile,v 1.10 2002/05/31 04:03:06 cthuang Exp $ + +debug: + tclsh &&| +set libDir [file join [info library] "../tcom"] +file copy -force Debug/tcom.dll $libDir +file copy -force dllserver_Debug/tcominproc.dll $libDir +file copy -force exeserver_Debug/tcomlocal.exe $libDir +set libDir [file join [info library] "../TclScript"] +file copy -force TclScript_Debug/TclScript.dll $libDir +| + +release: + tclsh &&| +set libDir [file join [info library] "../tcom"] +file copy -force Release/tcom.dll $libDir +file copy -force dllserver_Release/tcominproc.dll $libDir +file copy -force exeserver_Release/tcomlocal.exe $libDir +set libDir [file join [info library] "../TclScript"] +file copy -force TclScript_Release/TclScript.dll $libDir +| diff --git a/src/Reference.cpp b/src/Reference.cpp new file mode 100644 index 0000000..49e9a79 --- /dev/null +++ b/src/Reference.cpp @@ -0,0 +1,588 @@ +// $Id: Reference.cpp,v 1.69 2002/06/28 00:53:46 cthuang Exp $ +#pragma warning(disable: 4786) +#include +#include "ComObject.h" +#include "TypeLib.h" +#include "Uuid.h" +#include "Arguments.h" +#include "Reference.h" + +Reference::Connection::Connection (Tcl_Interp *interp, + IUnknown *pSource, + const Interface &eventInterfaceDesc, + TclObject servant) +{ + HRESULT hr; + + // Get connection point container. + IConnectionPointContainerPtr pContainer; + hr = pSource->QueryInterface( + IID_IConnectionPointContainer, + reinterpret_cast(&pContainer)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + // Find connection point. + hr = pContainer->FindConnectionPoint( + eventInterfaceDesc.iid(), &m_pConnectionPoint); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + // Create event sink. + ComObject *pComObject = ComObject::newInstance( + eventInterfaceDesc, + interp, + servant, + ""); + + // Connect to connection point. + hr = m_pConnectionPoint->Advise(pComObject->unknown(), &m_adviseCookie); + if (FAILED(hr)) { + _com_issue_error(hr); + } +} + +Reference::Connection::~Connection () +{ + m_pConnectionPoint->Unadvise(m_adviseCookie); + m_pConnectionPoint->Release(); +} + +Reference::Reference (IUnknown *pUnknown, const Interface *pInterface): + m_pUnknown(pUnknown), + m_pDispatch(0), + m_pInterface(pInterface), + m_pClass(0), + m_haveClsid(false) +{ } + +Reference::Reference ( + IUnknown *pUnknown, const Interface *pInterface, REFCLSID clsid): + m_pUnknown(pUnknown), + m_pDispatch(0), + m_pInterface(pInterface), + m_pClass(0), + m_clsid(clsid), + m_haveClsid(true) +{ } + +Reference::~Reference() +{ + unadvise(); + if (m_pDispatch != 0) { + m_pDispatch->Release(); + } + m_pUnknown->Release(); + delete m_pClass; +} + +IDispatch * +Reference::dispatch () +{ + if (m_pDispatch == 0) { + HRESULT hr = m_pUnknown->QueryInterface( + IID_IDispatch, reinterpret_cast(&m_pDispatch)); + if (FAILED(hr)) { + m_pDispatch = 0; + } + } + return m_pDispatch; +} + +const Class * +Reference::classDesc () +{ + if (!m_haveClsid) { + return 0; + } + + if (m_pClass == 0) { + TypeLib *pTypeLib = TypeLib::loadByClsid(m_clsid); + if (pTypeLib != 0) { + const Class *pClass = pTypeLib->findClass(m_clsid); + if (pClass != 0) { + m_pClass = new Class(*pClass); + } + } + delete pTypeLib; + } + + return m_pClass; +} + +void +Reference::advise (Tcl_Interp *interp, + const Interface &eventInterfaceDesc, + TclObject servant) +{ + m_connections.push_back(new Connection( + interp, m_pUnknown, eventInterfaceDesc, servant)); +} + +void +Reference::unadvise () +{ + for (Connections::iterator p = m_connections.begin(); + p != m_connections.end(); ++p) { + delete *p; + } + m_connections.clear(); +} + +bool +Reference::operator== (const Reference &rhs) const +{ + HRESULT hr; + + IUnknown *pUnknown1; + hr = m_pUnknown->QueryInterface( + IID_IUnknown, reinterpret_cast(&pUnknown1)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + IUnknown *pUnknown2; + rhs.m_pUnknown->QueryInterface( + IID_IUnknown, reinterpret_cast(&pUnknown2)); + if (FAILED(hr)) { + pUnknown1->Release(); + _com_issue_error(hr); + } + + bool result = pUnknown1 == pUnknown2; + + pUnknown1->Release(); + pUnknown2->Release(); + + return result; +} + +HRESULT +Reference::invokeDispatch ( + MEMBERID memberid, + WORD dispatchFlags, + const TypedArguments &arguments, + VARIANT *pResult) +{ + IDispatch *pDispatch = dispatch(); + if (pDispatch == 0) { + return E_NOINTERFACE; + } + + // Remove missing optional arguments from the end of the argument list. + // This permits calling servers which modify their action depending on + // the actual number of arguments. + DISPPARAMS *pParams = arguments.dispParams(); + + // Count the number of missing arguments. + unsigned cMissingArgs = 0; + VARIANT *pArg = pParams->rgvarg + pParams->cNamedArgs; + for (unsigned i = pParams->cNamedArgs; i < pParams->cArgs; ++i) { + if (V_VT(pArg) != VT_ERROR || V_ERROR(pArg) != DISP_E_PARAMNOTFOUND) { + break; + } + + ++cMissingArgs; + ++pArg; + } + + // Move the named arguments up next to the remaining unnamed arguments and + // adjust the DISPPARAMS struct. + if (cMissingArgs > 0) { + for (unsigned i = 0; i < pParams->cNamedArgs; ++i) { + pParams->rgvarg[i + cMissingArgs] = pParams->rgvarg[i]; + } + pParams->cArgs -= cMissingArgs; + pParams->rgvarg += cMissingArgs; + } + + EXCEPINFO excepInfo; + memset(&excepInfo, 0, sizeof(excepInfo)); + unsigned argErr; + + // Invoke through IDispatch interface. + HRESULT hr = pDispatch->Invoke( + memberid, + IID_NULL, + LOCALE_USER_DEFAULT, + dispatchFlags, + pParams, + pResult, + &excepInfo, + &argErr); + + if (hr == DISP_E_EXCEPTION) { + // Clean up exception information strings. + _bstr_t source(excepInfo.bstrSource, false); + _bstr_t description(excepInfo.bstrDescription, false); + _bstr_t helpFile(excepInfo.bstrHelpFile, false); + + hr = excepInfo.scode; + if (hr == 0) { + hr = _com_error::WCodeToHRESULT(excepInfo.wCode); + } + throw DispatchException(hr, description); + } + + return hr; +} + +HRESULT +Reference::invoke (MEMBERID memberid, + WORD dispatchFlags, + const TypedArguments &arguments, + VARIANT *pResult) +{ + if (m_pInterface != 0 && !m_pInterface->dispatchOnly()) { + EXCEPINFO excepInfo; + memset(&excepInfo, 0, sizeof(excepInfo)); + unsigned argErr; + + // Invoke through virtual function table. + ITypeInfo *pTypeInfo = interfaceDesc()->typeInfo(); + HRESULT hr = pTypeInfo->Invoke( + m_pUnknown, + memberid, + dispatchFlags, + arguments.dispParams(), + pResult, + &excepInfo, + &argErr); + + if (SUCCEEDED(hr)) { + return hr; + } + } + + return invokeDispatch(memberid, dispatchFlags, arguments, pResult); +} + +// IID of .NET Framework _Object interface +struct __declspec(uuid("65074F7F-63C0-304E-AF0A-D51741CB4A8D")) DotNetObject; + +const Interface * +Reference::findInterfaceFromDispatch (IUnknown *pUnknown) +{ + HRESULT hr; + + // See if the object implements IDispatch. + IDispatchPtr pDispatch; + hr = pUnknown->QueryInterface( + IID_IDispatch, reinterpret_cast(&pDispatch)); + if (FAILED(hr)) { + return 0; + } + + // Ask the IDispatch interface for type information. + unsigned count; + hr = pDispatch->GetTypeInfoCount(&count); + if (hr == E_NOTIMPL) { + return 0; + } + if (FAILED(hr)) { + _com_issue_error(hr); + } + if (count == 0) { + return 0; + } + + ITypeInfoPtr pTypeInfo; + hr = pDispatch->GetTypeInfo( + 0, + LOCALE_USER_DEFAULT, + &pTypeInfo); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + // Get the interface description. + TypeAttr typeAttr(pTypeInfo); + + if (IsEqualIID(typeAttr->guid, __uuidof(DotNetObject))) { + // The .NET Framework implements IDispatch::GetTypeInfo for classes + // declared with the attribute ClassInterface(ClassInterfaceType.None) + // by returning a description of the _Object interface. + return 0; + } + + const Interface *pInterface = + InterfaceManager::instance().newInterface(typeAttr->guid, pTypeInfo); + + if (pInterface->methods().empty() && pInterface->properties().empty()) { + // No invokable methods or properties where found in the interface + // description. + return 0; + } + return pInterface; +} + +const Interface * +Reference::findInterfaceFromClsid (REFCLSID clsid) +{ + const Interface *pInterface = 0; + + TypeLib *pTypeLib = TypeLib::loadByClsid(clsid); + if (pTypeLib != 0) { + const Class *pClass = pTypeLib->findClass(clsid); + if (pClass != 0) { + pInterface = pClass->defaultInterface(); + } + } + delete pTypeLib; + + return pInterface; +} + +const Interface * +Reference::findInterfaceFromIid (REFIID iid) +{ + const Interface *pInterface = 0; + + TypeLib *pTypeLib = TypeLib::loadByIid(iid); + if (pTypeLib != 0) { + pInterface = InterfaceManager::instance().find(iid); + } + delete pTypeLib; + + return pInterface; +} + +const Interface * +Reference::findInterface (IUnknown *pUnknown, REFCLSID clsid) +{ + const Interface *pInterface = 0; + + if (pUnknown != 0) { + pInterface = findInterfaceFromDispatch(pUnknown); + } + + if (pInterface == 0) { + pInterface = findInterfaceFromClsid(clsid); + } + + return pInterface; +} + +Reference * +Reference::createInstance ( + REFCLSID clsid, + const Interface *pInterface, + DWORD clsCtx, + const char *serverHost) +{ + // If we know it's a custom interface, then query for an interface pointer + // to that interface, otherwise query for an IUnknown interface. + const IID &iid = (pInterface == 0) ? IID_IUnknown : pInterface->iid(); + + HRESULT hr; + IUnknown *pUnknown; + + // Create an instance of the specified class. +#ifdef _WIN32_DCOM + if (serverHost == 0 + || serverHost[0] == '\0' + || strcmp(serverHost, "localhost") == 0 + || strcmp(serverHost, "127.0.0.1") == 0) { + // When creating an instance on the local machine, call + // CoCreateInstance instead of CoCreateInstanceEx with a null pointer + // to COSERVERINFO. This works around occasional failures in the RPC + // DLL on Windows NT 4.0, even when connecting to a server on the local + // machine. + hr = CoCreateInstance( + clsid, + NULL, + clsCtx, + iid, + reinterpret_cast(&pUnknown)); + } else { + COSERVERINFO serverInfo; + memset(&serverInfo, 0, sizeof(serverInfo)); + _bstr_t serverHostBstr(serverHost); + serverInfo.pwszName = serverHostBstr; + + MULTI_QI qi; + qi.pIID = &iid; + qi.pItf = NULL; + qi.hr = 0; + + hr = CoCreateInstanceEx( + clsid, + NULL, + clsCtx, + &serverInfo, + 1, + &qi); + if (SUCCEEDED(hr)) { + pUnknown = static_cast(qi.pItf); + } + } +#else + hr = CoCreateInstance( + clsid, + NULL, + clsCtx, + iid, + reinterpret_cast(&pUnknown)); +#endif + if (FAILED(hr)) { + _com_issue_error(hr); + } + + if (pInterface == 0) { + pInterface = findInterface(pUnknown, clsid); + if (pInterface != 0) { + // Get a pointer to the derived interface. + IUnknown *pNew; + hr = pUnknown->QueryInterface( + pInterface->iid(), + reinterpret_cast(&pNew)); + if (SUCCEEDED(hr)) { + pUnknown->Release(); + pUnknown = pNew; + } + } + } + + return new Reference(pUnknown, pInterface, clsid); +} + +Reference * +Reference::createInstance ( + const char *progId, + DWORD clsCtx, + const char *serverHost) +{ + // Convert the Prog ID to a CLSID. + CLSID clsid; + HRESULT hr = CLSIDFromProgID(_bstr_t(progId), &clsid); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + return createInstance(clsid, 0, clsCtx, serverHost); +} + +Reference * +Reference::getActiveObject (REFCLSID clsid, const Interface *pInterface) +{ + HRESULT hr; + + // Retrieve the instance of the object. + IUnknownPtr pActive; + hr = GetActiveObject(clsid, NULL, &pActive); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + // If we know it's a custom interface, then query for an interface pointer + // to that interface, otherwise query for an IUnknown interface. + IUnknown *pUnknown; + hr = pActive->QueryInterface( + (pInterface == 0) ? IID_IUnknown : pInterface->iid(), + reinterpret_cast(&pUnknown)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + if (pInterface == 0) { + pInterface = findInterface(pUnknown, clsid); + if (pInterface != 0) { + // Get a pointer to the derived interface. + IUnknown *pNew; + hr = pUnknown->QueryInterface( + pInterface->iid(), + reinterpret_cast(&pNew)); + if (SUCCEEDED(hr)) { + pUnknown->Release(); + pUnknown = pNew; + } + } + } + + return new Reference(pUnknown, pInterface, clsid); +} + +Reference * +Reference::getActiveObject (const char *progId) +{ + // Convert the Prog ID to a CLSID. + CLSID clsid; + HRESULT hr = CLSIDFromProgID(_bstr_t(progId), &clsid); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + return getActiveObject(clsid, 0); +} + +Reference * +Reference::getObject (const char *displayName) +{ + IUnknown *pUnknown; + HRESULT hr = CoGetObject( + _bstr_t(displayName), + NULL, + IID_IUnknown, + reinterpret_cast(&pUnknown)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + const Interface *pInterface = findInterfaceFromDispatch(pUnknown); + return new Reference(pUnknown, pInterface); +} + +Reference * +Reference::queryInterface (IUnknown *pOrig, REFIID iid) +{ + if (pOrig == 0) { + _com_issue_error(E_POINTER); + } + + IUnknown *pUnknown; + HRESULT hr = pOrig->QueryInterface( + iid, + reinterpret_cast(&pUnknown)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + const Interface *pInterface = 0; + if (!IsEqualIID(iid, IID_IDispatch)) { + pInterface = findInterfaceFromIid(iid); + if (pInterface == 0) { + pInterface = findInterfaceFromDispatch(pUnknown); + } + } + + return new Reference(pUnknown, pInterface); +} + +Reference * +Reference::newReference (IUnknown *pOrig, const Interface *pInterface) +{ + if (pOrig == 0) { + _com_issue_error(E_POINTER); + } + + if (pInterface == 0) { + pInterface = findInterfaceFromDispatch(pOrig); + } + + // If we know it's a custom interface, then query for an interface pointer + // to that interface, otherwise query for an IUnknown interface. + const IID &iid = (pInterface == 0) ? IID_IUnknown : pInterface->iid(); + + IUnknown *pUnknown; + HRESULT hr = pOrig->QueryInterface( + iid, + reinterpret_cast(&pUnknown)); + if (FAILED(hr)) { + pUnknown = pOrig; + pUnknown->AddRef(); + } + + return new Reference(pUnknown, pInterface); +} diff --git a/src/Reference.h b/src/Reference.h new file mode 100644 index 0000000..5c423d5 --- /dev/null +++ b/src/Reference.h @@ -0,0 +1,179 @@ +// $Id: Reference.h,v 1.41 2002/06/12 02:14:08 cthuang Exp $ +#ifndef REFERENCE_H +#define REFERENCE_H + +#include +#include "tcomApi.h" +#include "TclObject.h" +#include "TypeInfo.h" + +class TypedArguments; + +// Throw this exception when invoke returns DISP_E_EXCEPTION. + +class DispatchException +{ + SCODE m_scode; + _bstr_t m_description; + +public: + DispatchException (SCODE scode, const _bstr_t &description): + m_scode(scode), + m_description(description) + { } + + SCODE scode () const + { return m_scode; } + + const _bstr_t &description () const + { return m_description; } +}; + +// This class holds an interface pointer and the interface description needed +// to invoke methods on it. + +class TCOM_API Reference +{ + // This represents a connection from a connection point to an event sink. + + class TCOM_API Connection + { + // pointer to connection point + IConnectionPoint *m_pConnectionPoint; + + // cookie returned from Advise + DWORD m_adviseCookie; + + public: + // Create an event sink object and connect it to the connection point. + Connection( + Tcl_Interp *interp, + IUnknown *pSource, + const Interface &eventInterfaceDesc, + TclObject servant); + + // Disconnect from the connection point and release the pointer to the + // connection point. + ~Connection(); + }; + + // collection of event connections + typedef std::vector Connections; + Connections m_connections; + + // interface pointer to the object + IUnknown *m_pUnknown; + + // this pointer is non-null if the object implements IDispatch + IDispatch *m_pDispatch; + + // interface description includes information about methods and properties + const Interface *m_pInterface; + + // class description includes information about interfaces exposed + Class *m_pClass; + + // CLSID of the class the COM object implements + CLSID m_clsid; + + // true if we know the CLSID of the class the COM object implements + bool m_haveClsid; + + // The constructor assumes the reference count on the interface pointer has + // already been incremented. This object will decrement the reference + // count when it is destroyed. + Reference(IUnknown *pUnknown, const Interface *pInterface); + Reference( + IUnknown *pUnknown, const Interface *pInterface, REFCLSID clsid); + + // Do not allow instances of this class to be copied. + Reference(const Reference &rhs); + Reference &operator=(const Reference &rhs); + + // Try to get interface description from IDispatch object. + static const Interface *findInterfaceFromDispatch(IUnknown *pUnknown); + + // Try to get interface description from type library specified by CLSID. + static const Interface *findInterfaceFromClsid(REFCLSID clsid); + + // Try to get interface description from type library specified by IID. + static const Interface *findInterfaceFromIid(REFIID iid); + + // Get description of interface implemented by the object. + static const Interface *findInterface(IUnknown *pUnknown, REFCLSID clsid); + +public: + // destructor + ~Reference(); + + // Perform a QueryInterface on the interface pointer and create a reference. + static Reference *newReference( + IUnknown *pUnknown, const Interface *pInterface=0); + + // Perform a QueryInterface on the interface pointer and create a reference. + static Reference *queryInterface(IUnknown *pUnknown, REFIID iid); + + // Create an object using CoCreateInstance and construct a reference. + static Reference *createInstance( + REFCLSID clsid, + const Interface *pInterface, + DWORD clsCtx, + const char *serverHost); + + // Create an object using CoCreateInstance and construct a reference. + static Reference *createInstance( + const char *progId, DWORD clsCtx, const char *serverHost); + + // Get an object using GetActiveObject and construct a reference. + static Reference *getActiveObject( + REFCLSID clsid, const Interface *pInterface); + + // Get an object using GetActiveObject and construct a reference. + static Reference *getActiveObject(const char *progId); + + // Get an object using CoGetObject and construct a reference. + static Reference *getObject(const char *displayName); + + // Get raw interface pointer. + IUnknown *unknown () const + { return m_pUnknown; } + + // If the object implements IDispatch, return an IDispatch pointer, + // else return 0. + IDispatch *dispatch(); + + // Get interface description. + const Interface *interfaceDesc () const + { return m_pInterface; } + + // Get class description. + const Class *classDesc(); + + // Invoke a method or property using IDispatch. + HRESULT invokeDispatch( + MEMBERID memberid, + WORD dispatchFlags, + const TypedArguments &arguments, + VARIANT *pResult); + + // Invoke a method or property. + HRESULT invoke( + MEMBERID memberid, + WORD dispatchFlags, + const TypedArguments &arguments, + VARIANT *pResult); + + // Create an event sink object and connect it to the connection point. + void advise( + Tcl_Interp *interp, + const Interface &eventInterfaceDesc, + TclObject servant); + + // Disconnect all connected event sink objects. + void unadvise(); + + // Compare for COM identity. + bool operator==(const Reference &rhs) const; +}; + +#endif diff --git a/src/RegistryKey.cpp b/src/RegistryKey.cpp new file mode 100644 index 0000000..4a9f3be --- /dev/null +++ b/src/RegistryKey.cpp @@ -0,0 +1,81 @@ +// $Id: RegistryKey.cpp,v 1.6 2001/11/28 16:10:57 cthuang Exp $ +#include "RegistryKey.h" + +void +RegistryKey::open (HKEY hkey, const std::string &subkeyName) +{ + LONG result = RegOpenKeyEx( + hkey, + subkeyName.c_str(), + 0, + KEY_READ, + &m_hkey); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("cannot read registry key " + subkeyName); + } +} + +RegistryKey::RegistryKey (HKEY hkey, const std::string &subkeyName) +{ + open(hkey, subkeyName); +} + +RegistryKey::RegistryKey (const RegistryKey &key, + const std::string &subkeyName) +{ + open(key.m_hkey, subkeyName); +} + +RegistryKey::~RegistryKey () +{ + RegCloseKey(m_hkey); +} + +std::string +RegistryKey::subkeyName (int index) +{ + char name[256]; + DWORD size = sizeof(name); + FILETIME lastWriteTime; + + LONG result = RegEnumKeyEx( + m_hkey, + index, + name, + &size, + NULL, + NULL, + NULL, + &lastWriteTime); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("RegEnumKeyEx"); + } + + return std::string(name); +} + +std::string +RegistryKey::value () +{ + return value(""); +} + +std::string +RegistryKey::value (const char *valueName) +{ + BYTE data[256]; + DWORD size = sizeof(data); + + LONG result = RegQueryValueEx( + m_hkey, + valueName, + NULL, + NULL, + data, + &size); + if (result != ERROR_SUCCESS) { + throw std::runtime_error("RegQueryValueEx"); + } + + return std::string(reinterpret_cast(data)); +} diff --git a/src/RegistryKey.h b/src/RegistryKey.h new file mode 100644 index 0000000..e1b06da --- /dev/null +++ b/src/RegistryKey.h @@ -0,0 +1,34 @@ +// $Id: RegistryKey.h,v 1.5 2001/11/28 16:10:57 cthuang Exp $ +#ifndef REGISTRYKEY_H +#define REGISTRYKEY_H + +#include +#include +#define WIN32_LEAN_AND_MEAN +#include + +// This class represents a registry key. + +class RegistryKey +{ + HKEY m_hkey; + + // Open registry key. + void open(HKEY hkey, const std::string &subkeyName); + +public: + RegistryKey(HKEY hkey, const std::string &subkeyName); + RegistryKey(const RegistryKey &key, const std::string &subkeyName); + ~RegistryKey(); + + // Get name of subkey under this key. + std::string subkeyName(int index); + + // Get data for default value under this key. + std::string value(); + + // Get data for value under this key. + std::string value(const char *valueName); +}; + +#endif diff --git a/src/Singleton.h b/src/Singleton.h new file mode 100644 index 0000000..7a6543a --- /dev/null +++ b/src/Singleton.h @@ -0,0 +1,59 @@ +// $Id: Singleton.h,v 1.9 2002/04/13 03:53:56 cthuang Exp $ +#ifndef SINGLETON_H +#define SINGLETON_H + +#include +#include "mutex.h" + +// This template class provides code to construct and destroy a singleton. + +template +class Singleton +{ + // singleton instance + static T *ms_pInstance; + + // used to synchronize construction of singleton instance + static Mutex ms_singletonMutex; + + // Delete the instance when exiting. + static void exitProc(ClientData clientData); + +public: + // Get instance. + static T &instance(); +}; + +template +T *Singleton::ms_pInstance = 0; + +template +Mutex Singleton::ms_singletonMutex; + +template +void +Singleton::exitProc (ClientData clientData) +{ + delete reinterpret_cast(clientData); +} + +template +T & +Singleton::instance () +{ + if (ms_pInstance == 0) { + LOCK_MUTEX(ms_singletonMutex) + if (ms_pInstance == 0) { + ms_pInstance = new T; + + // Install an exit handler to destroy the instance when Tcl exits + // instead of depending on the destruction of a static C++ object + // because the Tcl library may have been finalized before the + // destructor is called. + Tcl_CreateExitHandler(exitProc, ms_pInstance); + } + } + return *ms_pInstance; +} + +#endif diff --git a/src/SupportErrorInfo.cpp b/src/SupportErrorInfo.cpp new file mode 100644 index 0000000..e2dd645 --- /dev/null +++ b/src/SupportErrorInfo.cpp @@ -0,0 +1,27 @@ +// $Id: SupportErrorInfo.cpp,v 1.3 2001/07/17 02:24:08 cthuang Exp $ +#include "ComObject.h" +#include "SupportErrorInfo.h" + +STDMETHODIMP +SupportErrorInfo::QueryInterface (REFIID iid, void **ppv) +{ + return m_object.queryInterface(iid, ppv); +} + +STDMETHODIMP_(ULONG) +SupportErrorInfo::AddRef () +{ + return m_object.addRef(); +} + +STDMETHODIMP_(ULONG) +SupportErrorInfo::Release () +{ + return m_object.release(); +} + +STDMETHODIMP +SupportErrorInfo::InterfaceSupportsErrorInfo (REFIID iid) +{ + return m_object.implemented(iid); +} diff --git a/src/SupportErrorInfo.h b/src/SupportErrorInfo.h new file mode 100644 index 0000000..40d94b5 --- /dev/null +++ b/src/SupportErrorInfo.h @@ -0,0 +1,30 @@ +// $Id: SupportErrorInfo.h,v 1.3 2001/07/17 02:24:08 cthuang Exp $ +#ifndef SUPPORTERRORINFO_H +#define SUPPORTERRORINFO_H + +#include +#include "tcomApi.h" + +class TCOM_API ComObject; + +// This class implements ISupportErrorInfo. + +class SupportErrorInfo: public ISupportErrorInfo +{ + ComObject &m_object; + +public: + SupportErrorInfo (ComObject &object): + m_object(object) + { } + + // IUnknown implementation + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // ISupportErrorInfo implementation + STDMETHODIMP InterfaceSupportsErrorInfo(REFIID riid); +}; + +#endif diff --git a/src/TclInterp.cpp b/src/TclInterp.cpp new file mode 100644 index 0000000..0a2f93a --- /dev/null +++ b/src/TclInterp.cpp @@ -0,0 +1,124 @@ +// $Id: TclInterp.cpp,v 1.12 2002/04/13 03:53:56 cthuang Exp $ +#include +#include "RegistryKey.h" +#include "TclObject.h" +#include "TclInterp.h" + +TclInterp::TclInterp (): + m_hmodTcl(NULL), + m_interp(0) +{ } + +void +TclInterp::terminate () +{ + if (m_interp != 0) { + Tcl_DeleteInterp(m_interp); + } + + if (m_hmodTcl != NULL) { + FreeLibrary(m_hmodTcl); + } +} + +void +TclInterp::initialize (const std::string &dllPath) +{ + if (m_interp == 0) { + doInitialize(dllPath); + } +} + +// Load the Tcl DLL. First try to load from the given path. If that +// fails, look for the Tcl DLL path in the registry and try loading it. + +static HINSTANCE +loadTclLibrary (const std::string &firstDllPath, std::string &foundDllPath) +{ + if (!firstDllPath.empty()) { + _bstr_t path(firstDllPath.c_str()); + HINSTANCE hmod = LoadLibrary(path); + if (hmod != NULL) { + foundDllPath = firstDllPath; + return hmod; + } + } + + std::string activeTclKeyName("SOFTWARE\\ActiveState\\ActiveTcl"); + RegistryKey activeTclKey(HKEY_LOCAL_MACHINE, activeTclKeyName); + std::string currentVersion = activeTclKey.value("CurrentVersion"); + + std::istringstream iss(currentVersion); + int major, minor; + char dot; + iss >> major >> dot >> minor; + + std::string versionKeyName(activeTclKeyName); + versionKeyName += "\\"; + versionKeyName += currentVersion; + RegistryKey versionKey(HKEY_LOCAL_MACHINE, versionKeyName); + + std::ostringstream oss; + oss << versionKey.value() << "\\bin\\tcl" << major << minor << ".dll"; + std::string dllPath(oss.str()); + + _bstr_t path(dllPath.c_str()); + HINSTANCE hmod = LoadLibrary(path); + if (hmod != NULL) { + foundDllPath = dllPath; + } + return hmod; +} + +void +TclInterp::doInitialize (const std::string &firstDllPath) +{ + // Load Tcl library. + std::string dllPath; + m_hmodTcl = loadTclLibrary(firstDllPath, dllPath); + if (m_hmodTcl == NULL) { + throw std::runtime_error("LoadLibrary"); + } + + // Get address of Tcl_CreateInterp function. + typedef Tcl_Interp *(*CreateInterpFunc)(); + CreateInterpFunc createInterp = reinterpret_cast( + GetProcAddress(m_hmodTcl, "Tcl_CreateInterp")); + if (createInterp == NULL) { + throw std::runtime_error("GetProcAddress Tcl_CreateInterp"); + } + + // Create Tcl interpreter. + m_interp = createInterp(); + if (Tcl_InitStubs(m_interp, "8.1", 0) == NULL) { + throw std::runtime_error("Tcl_InitStubs"); + } + + Tcl_FindExecutable(dllPath.c_str()); + + // Find and source Tcl initialization script. + if (Tcl_Init(m_interp) != TCL_OK) { + throw std::runtime_error(Tcl_GetStringResult(m_interp)); + } +} + +int +TclInterp::eval (const std::string &script) +{ + return Tcl_EvalEx( + m_interp, + const_cast(script.data()), + script.size(), + TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL); +} + +int +TclInterp::eval (TclObject script, TclObject *pResult) +{ + int completionCode = Tcl_EvalObjEx( + m_interp, script, TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL); + if (pResult != 0) { + *pResult = Tcl_GetObjResult(m_interp); + } + return completionCode; +} diff --git a/src/TclInterp.h b/src/TclInterp.h new file mode 100644 index 0000000..bbfa522 --- /dev/null +++ b/src/TclInterp.h @@ -0,0 +1,50 @@ +// $Id: TclInterp.h,v 1.8 2002/04/13 03:53:56 cthuang Exp $ +#ifndef TCLINTERP_H +#define TCLINTERP_H + +#include +#include + +class TclObject; + +// This class provides access to a Tcl interpreter loaded from a DLL. + +class TclInterp +{ + HINSTANCE m_hmodTcl; + Tcl_Interp *m_interp; + + // Load and initialize interpreter. + void doInitialize(const std::string &dllPath); + + // Do not allow others to copy instances of this class. + TclInterp(const TclInterp &); // not implemented + void operator=(const TclInterp &); // not implemented + +public: + TclInterp(); + + // Load Tcl DLL and create interpreter. + void initialize(const std::string &dllPath); + + // Delete interpreter and unload Tcl DLL. + void terminate(); + + // Evaluate script. + int eval(const std::string &script); + int eval(TclObject script, TclObject *pResult=0); + + // Get interpreter result as a string. + const char *resultString() const + { return Tcl_GetStringResult(m_interp); } + +#if 0 + // Get variable value. + int getVariable(const char *name, TclObject *pValue) const; + + // Set variable value. + int setVariable(const char *name, TclObject value); +#endif +}; + +#endif diff --git a/src/TclModule.cpp b/src/TclModule.cpp new file mode 100644 index 0000000..3a4f05c --- /dev/null +++ b/src/TclModule.cpp @@ -0,0 +1,42 @@ +// $Id: TclModule.cpp,v 1.5 2002/04/13 03:53:56 cthuang Exp $ +#pragma warning(disable: 4786) +#include "TclObject.h" +#include "TclModule.h" +#include "RegistryKey.h" + +int +TclModule::registerFactoryByScript (const std::string &clsid) +{ + // Get registry key containing initialization data. + std::string subkeyName("CLSID\\"); + subkeyName += clsid; + subkeyName += "\\tcom"; + RegistryKey extensionKey(HKEY_CLASSES_ROOT, subkeyName); + + // Initialize Tcl interpreter. + std::string tclDllPath; + try { + tclDllPath = extensionKey.value("TclDLL"); + } + catch (std::runtime_error &) + { } + + m_interp.initialize(tclDllPath); + + // Execute Tcl script which should register a class factory. + std::string script = extensionKey.value("Script"); + int completionCode = m_interp.eval(script); + if (completionCode != TCL_OK) { + const char *errMsg = m_interp.resultString(); + MessageBox(NULL, errMsg, "tcom Server Error", MB_OK); + } + + return completionCode; +} + +void +TclModule::terminate () +{ + revokeFactories(); + m_interp.terminate(); +} diff --git a/src/TclModule.h b/src/TclModule.h new file mode 100644 index 0000000..af95d6b --- /dev/null +++ b/src/TclModule.h @@ -0,0 +1,29 @@ +// $Id: TclModule.h,v 1.4 2002/04/13 03:53:56 cthuang Exp $ +#ifndef TCLMODULE_H +#define TCLMODULE_H + +#include "ComModule.h" +#include "TclInterp.h" + +// This is a COM module used to implement COM objects in Tcl. + +class TclModule: public ComModule +{ + TclInterp m_interp; + +protected: + TclModule () + { } + +public: + // Register a class factory by executing a Tcl script associated with + // its CLSID. It's expected the Tcl script will register a class factory + // using the "::tcom::object registerfactory" command. + // Returns a Tcl completion code. + int registerFactoryByScript(const std::string &clsid); + + // Shut down server. + void terminate(); +}; + +#endif diff --git a/src/TclObject.cpp b/src/TclObject.cpp new file mode 100644 index 0000000..dc164f7 --- /dev/null +++ b/src/TclObject.cpp @@ -0,0 +1,610 @@ +// $Id: TclObject.cpp,v 1.29 2002/05/31 04:03:06 cthuang Exp $ +#include "TclObject.h" +#ifdef WIN32 +#include "Extension.h" +#include "Reference.h" +#endif + +Tcl_ObjType *TclTypes::ms_pBooleanType; +Tcl_ObjType *TclTypes::ms_pDoubleType; +Tcl_ObjType *TclTypes::ms_pIntType; +Tcl_ObjType *TclTypes::ms_pListType; +#if TCL_MINOR_VERSION >= 1 +Tcl_ObjType *TclTypes::ms_pByteArrayType; +#endif + +void +TclTypes::initialize () +{ + // Don't worry about multiple threads initializing this data because they + // should all produce the same result anyway. + ms_pBooleanType = Tcl_GetObjType("boolean"); + ms_pDoubleType = Tcl_GetObjType("double"); + ms_pIntType = Tcl_GetObjType("int"); + ms_pListType = Tcl_GetObjType("list"); +#if TCL_MINOR_VERSION >= 1 + ms_pByteArrayType = Tcl_GetObjType("bytearray"); +#endif +} + + +TclObject::TclObject (): + m_pObj(Tcl_NewObj()) +{ Tcl_IncrRefCount(m_pObj); } + +TclObject::TclObject (const TclObject &rhs): + m_pObj(rhs.m_pObj) +{ Tcl_IncrRefCount(m_pObj); } + +TclObject::TclObject (Tcl_Obj *pObj): + m_pObj(pObj) +{ Tcl_IncrRefCount(m_pObj); } + +TclObject::TclObject (const char *src, int len): + m_pObj(Tcl_NewStringObj(const_cast(src), len)) +{ Tcl_IncrRefCount(m_pObj); } + +#if TCL_MINOR_VERSION >= 2 +TclObject::TclObject (const wchar_t *src, int len): + m_pObj(Tcl_NewUnicodeObj( + const_cast(reinterpret_cast(src)), + len)) +{ Tcl_IncrRefCount(m_pObj); } + +static Tcl_Obj * +newUnicodeObj (const Tcl_UniChar *pWide, int length) +{ + if (pWide == 0) { + return Tcl_NewObj(); + } + return Tcl_NewUnicodeObj(const_cast(pWide), length); +} +#endif + +TclObject::TclObject (const std::string &s): + m_pObj(Tcl_NewStringObj(const_cast(s.data()), s.size())) +{ Tcl_IncrRefCount(m_pObj); } + +TclObject::TclObject (bool value): + m_pObj(Tcl_NewBooleanObj(static_cast(value))) +{ Tcl_IncrRefCount(m_pObj); } + +TclObject::TclObject (int value): + m_pObj(Tcl_NewIntObj(value)) +{ Tcl_IncrRefCount(m_pObj); } + +TclObject::TclObject (long value): + m_pObj(Tcl_NewLongObj(value)) +{ Tcl_IncrRefCount(m_pObj); } + +TclObject::TclObject (double value): + m_pObj(Tcl_NewDoubleObj(value)) +{ Tcl_IncrRefCount(m_pObj); } + +TclObject::TclObject (int objc, Tcl_Obj *CONST objv[]): + m_pObj(Tcl_NewListObj(objc, objv)) +{ Tcl_IncrRefCount(m_pObj); } + +TclObject::~TclObject () +{ Tcl_DecrRefCount(m_pObj); } + +TclObject & +TclObject::operator= (const TclObject &rhs) +{ + Tcl_IncrRefCount(rhs.m_pObj); + Tcl_DecrRefCount(m_pObj); + m_pObj = rhs.m_pObj; + return *this; +} + +TclObject & +TclObject::operator= (Tcl_Obj *pObj) +{ + Tcl_IncrRefCount(pObj); + Tcl_DecrRefCount(m_pObj); + m_pObj = pObj; + return *this; +} + +bool +TclObject::getBool () const +{ + int value; + Tcl_GetBooleanFromObj(0, m_pObj, &value); + return value != 0; +} + +int +TclObject::getInt () const +{ + int value; + Tcl_GetIntFromObj(0, m_pObj, &value); + return value; +} + +long +TclObject::getLong () const +{ + long value; + Tcl_GetLongFromObj(0, m_pObj, &value); + return value; +} + +double +TclObject::getDouble () const +{ + double value; + Tcl_GetDoubleFromObj(0, m_pObj, &value); + return value; +} + +TclObject & +TclObject::lappend (Tcl_Obj *pElement) +{ + if (Tcl_IsShared(m_pObj)) { + Tcl_DecrRefCount(m_pObj); + m_pObj = Tcl_DuplicateObj(m_pObj); + Tcl_IncrRefCount(m_pObj); + } + Tcl_ListObjAppendElement(NULL, m_pObj, pElement); + // TODO: Should check for error result if conversion to list failed. + return *this; +} + +#ifdef WIN32 + +TclObject::TclObject (VARIANT *pSrc, const Type &type, Tcl_Interp *interp) +{ + HRESULT hr; + + if (V_VT(pSrc) & VT_ARRAY) { + // We can handle only one-dimensional arrays. + SAFEARRAY *psa = V_ARRAY(pSrc); + if (SafeArrayGetDim(psa) != 1) { + _com_issue_error(E_INVALIDARG); + } + + // Get index range. + long lowerBound; + hr = SafeArrayGetLBound(psa, 1, &lowerBound); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + long upperBound; + hr = SafeArrayGetUBound(psa, 1, &upperBound); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + // Get element type. + VARTYPE vt = V_VT(pSrc) & VT_TYPEMASK; + + switch (vt) { + case VT_UNKNOWN: + case VT_DISPATCH: + // Convert array of IUnknown to Tcl list of interface pointer + // handles. + { + IUnknown **pData; + hr = SafeArrayAccessData( + psa, reinterpret_cast(&pData)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + m_pObj = Tcl_NewListObj(0, 0); + for (long i = lowerBound; i <= upperBound; ++i) { + Tcl_Obj *pElement = + Extension::referenceHandles.newObj( + interp, Reference::newReference(pData[i])); + Tcl_ListObjAppendElement(interp, m_pObj, pElement); + } + + hr = SafeArrayUnaccessData(psa); + if (FAILED(hr)) { + _com_issue_error(hr); + } + } + break; + + case VT_UI1: + { + unsigned char *pData; + hr = SafeArrayAccessData( + psa, reinterpret_cast(&pData)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + long length = upperBound - lowerBound + 1; + m_pObj = +#if TCL_MINOR_VERSION >= 1 + // Convert array of bytes to Tcl byte array. + Tcl_NewByteArrayObj(pData, length); +#else + // Convert array of bytes to Tcl string. + Tcl_NewStringObj(reinterpret_cast(pData), length); +#endif + + hr = SafeArrayUnaccessData(psa); + if (FAILED(hr)) { + _com_issue_error(hr); + } + } + break; + + default: + // Convert array of other types to Tcl list of objects. + { + m_pObj = Tcl_NewListObj(0, 0); + for (long i = lowerBound; i <= upperBound; ++i) { + _variant_t elementVar; + + if (vt == VT_VARIANT) { + hr = SafeArrayGetElement(psa, &i, &elementVar); + } else { + // I hope the element can be contained in a VARIANT. + V_VT(&elementVar) = vt; + hr = SafeArrayGetElement( + psa, &i, &elementVar.punkVal); + } + if (FAILED(hr)) { + _com_issue_error(hr); + } + + TclObject element(&elementVar, type, interp); + Tcl_ListObjAppendElement(interp, m_pObj, element); + } + } + } + + } else { + switch (V_VT(pSrc)) { + case VT_BOOL: + m_pObj = Tcl_NewBooleanObj(V_BOOL(pSrc)); + break; + + case VT_I1: + case VT_UI1: + m_pObj = Tcl_NewLongObj(V_I1(pSrc)); + break; + + case VT_I2: + case VT_UI2: + m_pObj = Tcl_NewLongObj(V_I2(pSrc)); + break; + + case VT_I4: + case VT_UI4: + case VT_INT: + case VT_UINT: + m_pObj = Tcl_NewLongObj(V_I4(pSrc)); + break; + + case VT_R4: + m_pObj = Tcl_NewDoubleObj(V_R4(pSrc)); + break; + + case VT_DATE: + case VT_R8: + m_pObj = Tcl_NewDoubleObj(V_R8(pSrc)); + break; + + case VT_DISPATCH: + { + const Interface *pInterface = + InterfaceManager::instance().find(type.iid()); + m_pObj = Extension::referenceHandles.newObj( + interp, + Reference::newReference(V_DISPATCH(pSrc), pInterface)); + } + break; + + case VT_UNKNOWN: + { + const Interface *pInterface = + InterfaceManager::instance().find(type.iid()); + m_pObj = Extension::referenceHandles.newObj( + interp, + Reference::newReference(V_UNKNOWN(pSrc), pInterface)); + } + break; + + case VT_NULL: + m_pObj = Tcl_NewObj(); + break; + + case VT_LPSTR: + m_pObj = Tcl_NewStringObj(V_I1REF(pSrc), -1); + break; + + case VT_LPWSTR: + { +#if TCL_MINOR_VERSION >= 2 + // Uses Unicode function introduced in Tcl 8.2. + m_pObj = newUnicodeObj(V_UI2REF(pSrc), -1); +#else + const wchar_t *pWide = V_UI2REF(pSrc); + _bstr_t str(pWide); + m_pObj = Tcl_NewStringObj(str, -1); +#endif + } + break; + + default: + if (V_VT(pSrc) == VT_USERDEFINED && type.name() == "GUID") { + Uuid uuid(*static_cast(V_BYREF(pSrc))); + m_pObj = Tcl_NewStringObj( + const_cast(uuid.toString().c_str()), -1); + } else { + _bstr_t str(pSrc); +#if TCL_MINOR_VERSION >= 2 + // Uses Unicode function introduced in Tcl 8.2. + wchar_t *pWide = str; + m_pObj = newUnicodeObj( + reinterpret_cast(pWide), str.length()); +#else + m_pObj = Tcl_NewStringObj(str, -1); +#endif + } + } + } + + Tcl_IncrRefCount(m_pObj); +} + +BSTR +TclObject::getBSTR () const +{ +#if TCL_MINOR_VERSION >= 2 + // Uses Unicode function introduced in Tcl 8.2. + return SysAllocString(getUnicode()); +#else + _bstr_t str(c_str()); + return SysAllocString(str); +#endif +} + +void +TclObject::toVariant (VARIANT *pDest, + const Type &type, + Tcl_Interp *interp, + bool addRef) +{ + VariantClear(pDest); + VARTYPE vt = type.vartype(); + + Reference *pReference = Extension::referenceHandles.find(interp, m_pObj); + if (pReference != 0) { + // Convert interface pointer handle to interface pointer. + if (addRef) { + // Must increment reference count of interface pointers returned + // from methods. + pReference->unknown()->AddRef(); + } + + IDispatch *pDispatch = pReference->dispatch(); + if (pDispatch != 0) { + V_VT(pDest) = VT_DISPATCH; + V_DISPATCH(pDest) = pDispatch; + } else { + V_VT(pDest) = VT_UNKNOWN; + V_UNKNOWN(pDest) = pReference->unknown(); + } + + } else if (m_pObj->typePtr == &Extension::unknownPointerType) { + // Convert to interface pointer. + IUnknown *pUnknown = static_cast( + m_pObj->internalRep.otherValuePtr); + if (addRef) { + // Must increment reference count of interface pointers returned + // from methods. + pUnknown->AddRef(); + } + V_VT(pDest) = VT_UNKNOWN; + V_UNKNOWN(pDest) = pUnknown; + + } else if (vt == VT_SAFEARRAY) { + // Convert Tcl list to SAFEARRAY. + int numElements; + Tcl_Obj **pElements; + if (Tcl_ListObjGetElements(interp, m_pObj, &numElements, &pElements) + != TCL_OK) { + _com_issue_error(E_INVALIDARG); + } + + const Type &elementType = type.elementType(); + SAFEARRAY *psa = + SafeArrayCreateVector(elementType.vartype(), 0, numElements); + if (psa == 0) { + _com_issue_error(E_OUTOFMEMORY); + } + + void *pData; + HRESULT hr; + hr = SafeArrayAccessData(psa, &pData); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + for (int i = 0; i < numElements; ++i) { + TclObject value(pElements[i]); + + switch (elementType.vartype()) { + case VT_BOOL: + static_cast(pData)[i] = + value.getBool() ? VARIANT_TRUE : VARIANT_FALSE; + break; + + case VT_R4: + static_cast(pData)[i] = + static_cast(value.getDouble()); + break; + + case VT_R8: + static_cast(pData)[i] = value.getDouble(); + break; + + case VT_BSTR: + static_cast(pData)[i] = value.getBSTR(); + break; + + case VT_VARIANT: + { + VARIANT *pDest = static_cast(pData) + i; + VariantInit(pDest); + value.toVariant(pDest, elementType, interp); + } + break; + + default: + static_cast(pData)[i] = value.getLong(); + } + } + + hr = SafeArrayUnaccessData(psa); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + V_VT(pDest) = VT_ARRAY | elementType.vartype(); + V_ARRAY(pDest) = psa; + + } else if (m_pObj->typePtr == TclTypes::listType()) { + // Convert Tcl list to array of VARIANT. + int numElements; + Tcl_Obj **pElements; + if (Tcl_ListObjGetElements(interp, m_pObj, &numElements, &pElements) + != TCL_OK) { + _com_issue_error(E_INVALIDARG); + } + + SAFEARRAY *psa = SafeArrayCreateVector(VT_VARIANT, 0, numElements); + if (psa == 0) { + _com_issue_error(E_OUTOFMEMORY); + } + + VARIANT *pData; + HRESULT hr; + hr = SafeArrayAccessData(psa, reinterpret_cast(&pData)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + for (int i = 0; i < numElements; ++i) { + TclObject value(pElements[i]); + VariantInit(&pData[i]); + value.toVariant(&pData[i], Type::variant(), interp, addRef); + } + + hr = SafeArrayUnaccessData(psa); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + V_VT(pDest) = VT_ARRAY | VT_VARIANT; + V_ARRAY(pDest) = psa; + +#if TCL_MINOR_VERSION >= 1 + } else if (m_pObj->typePtr == TclTypes::byteArrayType()) { + // Convert Tcl byte array to array of bytes. + int length; + unsigned char *pBytes = Tcl_GetByteArrayFromObj(m_pObj, &length); + + SAFEARRAY *psa = SafeArrayCreateVector(VT_UI1, 0, length); + if (psa == 0) { + _com_issue_error(E_OUTOFMEMORY); + } + + unsigned char *pDestData; + HRESULT hr; + hr = SafeArrayAccessData( + psa, reinterpret_cast(&pDestData)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + memcpy(pDestData, pBytes, length); + + hr = SafeArrayUnaccessData(psa); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + V_VT(pDest) = VT_ARRAY | VT_UI1; + V_ARRAY(pDest) = psa; +#endif + + } else if (m_pObj->typePtr == &Extension::naType) { + // This variant indicates a missing optional argument. + VariantCopy(pDest, &vtMissing); + + } else if (m_pObj->typePtr == &Extension::nullType) { + V_VT(pDest) = VT_NULL; + + } else if (m_pObj->typePtr == TclTypes::intType()) { + long value; + if (Tcl_GetLongFromObj(interp, m_pObj, &value) != TCL_OK) { + value = 0; + } + V_VT(pDest) = VT_I4; + V_I4(pDest) = value; + + if (vt != VT_VARIANT && vt != VT_USERDEFINED) { + VariantChangeType(pDest, pDest, 0, vt); + } + + } else if (m_pObj->typePtr == TclTypes::doubleType()) { + double value; + if (Tcl_GetDoubleFromObj(interp, m_pObj, &value) != TCL_OK) { + value = 0.0; + } + V_VT(pDest) = VT_R8; + V_R8(pDest) = value; + + if (vt != VT_VARIANT && vt != VT_USERDEFINED) { + VariantChangeType(pDest, pDest, 0, vt); + } + + } else if (m_pObj->typePtr == TclTypes::booleanType()) { + int value; + if (Tcl_GetBooleanFromObj(interp, m_pObj, &value) != TCL_OK) { + value = 0; + } + V_VT(pDest) = VT_BOOL; + V_BOOL(pDest) = (value != 0) ? VARIANT_TRUE : VARIANT_FALSE; + + if (vt != VT_VARIANT && vt != VT_USERDEFINED) { + VariantChangeType(pDest, pDest, 0, vt); + } + + } else if (vt == VT_BOOL) { + V_VT(pDest) = VT_BOOL; + V_BOOL(pDest) = getBool() ? VARIANT_TRUE : VARIANT_FALSE; + + } else { +#if TCL_MINOR_VERSION >= 2 + // Uses Unicode function introduced in Tcl 8.2. + const wchar_t *pStringRep = + reinterpret_cast(Tcl_GetUnicode(m_pObj)); +#else + const char *pStringRep = Tcl_GetStringFromObj(m_pObj, 0); +#endif + _variant_t var(pStringRep); + + // If trying to convert from a string to a date, + // we need to convert to a double (VT_R8) first. + if (vt == VT_DATE) { + var.ChangeType(VT_R8); + } + + // Try to convert from a string representation. + if (vt != VT_VARIANT && vt != VT_USERDEFINED && vt != VT_LPWSTR) { + var.ChangeType(vt); + } + VariantCopy(pDest, &var); + } +} + +#endif diff --git a/src/TclObject.h b/src/TclObject.h new file mode 100644 index 0000000..9a71502 --- /dev/null +++ b/src/TclObject.h @@ -0,0 +1,123 @@ +// $Id: TclObject.h,v 1.12 2002/04/12 02:55:28 cthuang Exp $ +#ifndef TCLOBJECT_H +#define TCLOBJECT_H + +#ifdef WIN32 +#include "TypeInfo.h" +#endif +#include +#include +#include "tcomApi.h" + +// This class provides access to Tcl's built-in internal representation types. + +class TclTypes +{ + static Tcl_ObjType *ms_pBooleanType; + static Tcl_ObjType *ms_pDoubleType; + static Tcl_ObjType *ms_pIntType; + static Tcl_ObjType *ms_pListType; + +public: + static void initialize(); + + static Tcl_ObjType *booleanType () + { return ms_pBooleanType; } + + static Tcl_ObjType *doubleType () + { return ms_pDoubleType; } + + static Tcl_ObjType *intType () + { return ms_pIntType; } + + static Tcl_ObjType *listType () + { return ms_pListType; } + +#if TCL_MINOR_VERSION >= 1 +private: + static Tcl_ObjType *ms_pByteArrayType; + +public: + static Tcl_ObjType *byteArrayType () + { return ms_pByteArrayType; } +#endif +}; + +// This class wraps a Tcl_Obj pointer to provide copy and value semantics by +// automatically incrementing and decrementing its reference count. + +class TCOM_API TclObject +{ + Tcl_Obj *m_pObj; + +public: + TclObject(); + TclObject(const TclObject &rhs); + TclObject(Tcl_Obj *pObj); + TclObject(const char *src, int len = -1); + TclObject(const std::string &s); + TclObject(bool value); + TclObject(int value); + TclObject(long value); + TclObject(double value); + TclObject(int objc, Tcl_Obj *CONST objv[]); + ~TclObject(); + + TclObject &operator=(const TclObject &rhs); + TclObject &operator=(Tcl_Obj *pObj); + + // Get raw object pointer. + operator Tcl_Obj * () const + { return const_cast(m_pObj); } + + // Get UTF-8 string representation of the object. + const char *c_str () const + { return Tcl_GetStringFromObj(const_cast(m_pObj), 0); } + +#if TCL_MINOR_VERSION >= 2 + // Construct Unicode string value. + TclObject(const wchar_t *src, int len = -1); + + // Get Unicode string representation of the object. + const wchar_t *getUnicode() const + { return reinterpret_cast( + Tcl_GetUnicode(const_cast(m_pObj))); } +#endif + + // Convert object to bool and return value. + bool getBool() const; + + // Convert object to int and return value. + int getInt() const; + + // Convert object to long and return value. + long getLong() const; + + // Convert object to double and return value. + double getDouble() const; + + // Convert the object to a list if it's not already a list, + // and then append the element to the end of the list. + TclObject &lappend(Tcl_Obj *pElement); + +#ifdef WIN32 + // Construct Tcl object from VARIANT value. + TclObject( + VARIANT *pSrc, // VARIANT value to convert from + const Type &type, // expected type for interface pointers + Tcl_Interp *interp); + + // Convert Tcl object to VARIANT value. + void toVariant( + VARIANT *pDest, // converted value put here + const Type &type, // desired data type + Tcl_Interp *interp, + bool addRef=false); // call AddRef on interface pointer + + // Get BSTR representation. Caller is responsible for freeing the + // returned BSTR. + BSTR getBSTR() const; +#endif +}; + +#endif diff --git a/src/TclScript.cpp b/src/TclScript.cpp new file mode 100644 index 0000000..71954c6 --- /dev/null +++ b/src/TclScript.cpp @@ -0,0 +1,230 @@ +// $Id: TclScript.cpp,v 1.10 2002/07/14 18:42:57 cthuang Exp $ +#include "ActiveScriptError.h" +#include "Reference.h" +#include "TypeInfo.h" +#include "Extension.h" +#include "tclRunTime.h" + +#define NAMESPACE "::TclScriptEngine::" +#define ENGINE_PACKAGE_NAME "TclScript" +#define ENGINE_PACKAGE_VERSION "1.0" + +static int +outputdebugCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "message"); + return TCL_ERROR; + } + + Tcl_Obj *pMessage = objv[1]; + if (Tcl_IsShared(pMessage)) { + pMessage = Tcl_DuplicateObj(pMessage); + } + Tcl_IncrRefCount(pMessage); + Tcl_AppendToObj(pMessage, "\n", 1); + OutputDebugString(Tcl_GetStringFromObj(pMessage, 0)); + Tcl_DecrRefCount(pMessage); + return TCL_OK; +} + +static int +getnameditemCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 3 || objc > 4) { + Tcl_WrongNumArgs( + interp, 1, objv, "scriptSiteHandle itemName ?subItemName?"); + return TCL_ERROR; + } + + Reference *pRef = Extension::referenceHandles.find(interp, objv[1]); + if (pRef == 0) { + const char *arg = Tcl_GetStringFromObj(objv[1], 0); + Tcl_AppendResult(interp, "invalid handle ", arg, NULL); + return TCL_ERROR; + } + + try { + HRESULT hr; + + IActiveScriptSitePtr pScriptSite; + hr = pRef->unknown()->QueryInterface( + IID_IActiveScriptSite, reinterpret_cast(&pScriptSite)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + TclObject itemName(objv[2]); + IUnknownPtr pUnknown; + hr = pScriptSite->GetItemInfo( + itemName.getUnicode(), SCRIPTINFO_IUNKNOWN, &pUnknown, 0); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + TclObject subItemName; + if (objc == 4) { + subItemName = objv[3]; + } + + int subItemNameLength; + Tcl_GetStringFromObj(subItemName, &subItemNameLength); + + Reference *pNewRef; + if (subItemNameLength == 0) { + pNewRef = Reference::newReference(pUnknown); + } else { + IDispatchPtr pDispatch; + hr = pUnknown->QueryInterface( + IID_IDispatch, reinterpret_cast(&pDispatch)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + // Get the DISPID of the name. + wchar_t *wideSubItemName = const_cast( + subItemName.getUnicode()); + + DISPID dispid; + hr = pDispatch->GetIDsOfNames( + IID_NULL, + &wideSubItemName, + 1, + LOCALE_USER_DEFAULT, + &dispid); + if (FAILED(hr)) { + // If we didn't find the name, then return an empty Tcl result. + Tcl_ResetResult(interp); + return TCL_OK; + } + + // Try to get the property. + EXCEPINFO excepInfo; + memset(&excepInfo, 0, sizeof(excepInfo)); + UINT argErr = 0; + + _variant_t returnValue; + + DISPPARAMS dispParams; + dispParams.rgvarg = NULL; + dispParams.rgdispidNamedArgs = NULL; + dispParams.cArgs = 0; + dispParams.cNamedArgs = 0; + + hr = pDispatch->Invoke( + dispid, + IID_NULL, + LOCALE_USER_DEFAULT, + DISPATCH_PROPERTYGET, + &dispParams, + &returnValue, + &excepInfo, + &argErr); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + if (V_VT(&returnValue) != VT_DISPATCH) { + Tcl_AppendResult(interp, "sub item is not an IDispatch", NULL); + return TCL_ERROR; + } + + pNewRef = Reference::newReference(V_DISPATCH(&returnValue)); + } + + Tcl_SetObjResult( + interp, Extension::referenceHandles.newObj(interp, pNewRef)); + } + catch (_com_error &e) { + return Extension::setComErrorResult(interp, e, __FILE__, __LINE__); + } + + return TCL_OK; +} + +static int +activescripterrorCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 7) { + Tcl_WrongNumArgs( + interp, + 1, + objv, + "hresult source description lineNum charPos sourceLineText"); + return TCL_ERROR; + } + + TclObject hresult(objv[1]); + TclObject source(objv[2]); + TclObject description(objv[3]); + TclObject lineNumber(objv[4]); + TclObject characterPosition(objv[5]); + TclObject sourceLineText(objv[6]); + + try { + ActiveScriptError *pActiveScriptError = new ActiveScriptError( + hresult.getLong(), + source.c_str(), + description.c_str(), + lineNumber.getLong(), + characterPosition.getLong(), + sourceLineText.c_str()); + + Tcl_Obj *pResult = Tcl_NewObj(); + Tcl_InvalidateStringRep(pResult); + pResult->typePtr = &Extension::unknownPointerType; + pResult->internalRep.otherValuePtr = pActiveScriptError; + + Tcl_SetObjResult(interp, pResult); + } + catch (_com_error &e) { + return Extension::setComErrorResult(interp, e, __FILE__, __LINE__); + } + + return TCL_OK; +} + +extern "C" DLLEXPORT int +Tclscript_Init (Tcl_Interp *interp) +{ +#ifdef USE_TCL_STUBS + // Stubs were introduced in Tcl 8.1. + if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { + return TCL_ERROR; + } +#endif + + Tcl_CreateObjCommand( + interp, NAMESPACE "outputdebug", outputdebugCmd, 0, 0); + Tcl_CreateObjCommand( + interp, NAMESPACE "getnameditem", getnameditemCmd, 0, 0); + Tcl_CreateObjCommand( + interp, NAMESPACE "activescripterror", activescripterrorCmd, 0, 0); + + return Tcl_PkgProvide(interp, ENGINE_PACKAGE_NAME, ENGINE_PACKAGE_VERSION); +} + +extern "C" DLLEXPORT int +Tclscript_SafeInit (Tcl_Interp *interp) +{ +#ifdef USE_TCL_STUBS + // Stubs were introduced in Tcl 8.1. + if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { + return TCL_ERROR; + } +#endif + + return Tcl_PkgProvide(interp, ENGINE_PACKAGE_NAME, ENGINE_PACKAGE_VERSION); +} diff --git a/src/TclScript.dsp b/src/TclScript.dsp new file mode 100644 index 0000000..12f542d --- /dev/null +++ b/src/TclScript.dsp @@ -0,0 +1,125 @@ +# Microsoft Developer Studio Project File - Name="TclScript" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=TclScript - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "TclScript.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "TclScript.mak" CFG="TclScript - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "TclScript - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "TclScript - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "TclScript - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "TclScript___Win32_Release" +# PROP BASE Intermediate_Dir "TclScript___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "TclScript_Release" +# PROP Intermediate_Dir "TclScript_Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DLLSERVER_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /Od /I "\tcl\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DLLSERVER_EXPORTS" /D "TCL_THREADS" /D "USE_TCL_STUBS" /D "USE_NON_CONST" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +# SUBTRACT MTL /mktyplib203 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 Release\tcom.lib rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /libpath:"\tcl\lib" + +!ELSEIF "$(CFG)" == "TclScript - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "TclScript___Win32_Debug" +# PROP BASE Intermediate_Dir "TclScript___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "TclScript_Debug" +# PROP Intermediate_Dir "TclScript_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DLLSERVER_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "\tcl\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DLLSERVER_EXPORTS" /D "TCL_THREADS" /D "USE_TCL_STUBS" /D "USE_NON_CONST" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /win32 +# SUBTRACT MTL /mktyplib203 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 Debug\tcom.lib rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /libpath:"\tcl\lib" + +!ENDIF + +# Begin Target + +# Name "TclScript - Win32 Release" +# Name "TclScript - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\ActiveScriptError.cpp +# End Source File +# Begin Source File + +SOURCE=.\TclScript.cpp +# End Source File +# Begin Source File + +SOURCE=.\TclScript.idl +# End Source File +# Begin Source File + +SOURCE=.\TclScriptVersion.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\ActiveScriptError.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/src/TclScript.idl b/src/TclScript.idl new file mode 100644 index 0000000..1dbdf31 --- /dev/null +++ b/src/TclScript.idl @@ -0,0 +1,27 @@ +import "activscp.idl"; +import "objsafe.idl"; + +#if _MSC_VER >= 1300 +#define IActiveScriptParse IActiveScriptParse32 +#endif + +[ + uuid(0A0059B8-E0B0-11D2-942A-00C04F7040AC), + version(1.0), + helpstring("TclScript 1.0 Type Library") +] +library TclScript +{ + importlib("stdole32.tlb"); + + [ + uuid(0A0059C5-E0B0-11D2-942A-00C04F7040AD), + helpstring("Engine Class") + ] + coclass Engine + { + [default] interface IActiveScript; + interface IActiveScriptParse; + interface IObjectSafety; + }; +}; diff --git a/src/TclScriptVersion.rc b/src/TclScriptVersion.rc new file mode 100644 index 0000000..914a688 --- /dev/null +++ b/src/TclScriptVersion.rc @@ -0,0 +1,35 @@ +// $Id: TclScriptVersion.rc,v 1.3 2002/04/27 18:15:24 cthuang Exp $ +#include +#include "version.h" +#include "buildNumber.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION PACKAGE_MAJOR_VERSION,PACKAGE_MINOR_VERSION,0,BUILD_NUMBER + PRODUCTVERSION PACKAGE_MAJOR_VERSION,PACKAGE_MINOR_VERSION,0,BUILD_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0 +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "Tcl script engine" + VALUE "FileVersion", PACKAGE_VERSION + VALUE "LegalCopyright", "Copyright 2002 by Chin Huang" + VALUE "OriginalFilename", "TclScript.dll" + VALUE "ProductName", "Tcl script engine" + VALUE "ProductVersion", PACKAGE_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/ThreadLocalStorage.h b/src/ThreadLocalStorage.h new file mode 100644 index 0000000..71770d1 --- /dev/null +++ b/src/ThreadLocalStorage.h @@ -0,0 +1,64 @@ +// $Id: ThreadLocalStorage.h,v 1.1 2002/04/20 15:43:57 cthuang Exp $ +#ifndef THREADLOCALSTORAGE_H +#define THREADLOCALSTORAGE_H + +#include "mutex.h" + +// This factory creates an instance of type T for each calling thread. + +template +class ThreadLocalStorage +{ + // used to synchronize initialization of index + Mutex m_mutex; + + DWORD m_index; + bool m_initialized; + + // not implemented + ThreadLocalStorage(const ThreadLocalStorage &); + void operator=(const ThreadLocalStorage &); + +public: + ThreadLocalStorage(); + ~ThreadLocalStorage(); + + // Get instance specific to the calling thread. + T &instance() const; +}; + +template +ThreadLocalStorage::ThreadLocalStorage () +{ + LOCK_MUTEX(m_mutex) + + if (!m_initialized) { + m_index = TlsAlloc(); + m_initialized = true; + } +} + +template +ThreadLocalStorage::~ThreadLocalStorage () +{ + LOCK_MUTEX(m_mutex) + + if (m_initialized) { + TlsFree(m_index); + m_initialized = false; + } +} + +template +T & +ThreadLocalStorage::instance () const +{ + T *pValue = static_cast(TlsGetValue(m_index)); + if (pValue == 0) { + pValue = new T; + TlsSetValue(m_index, pValue); + } + return *pValue; +} + +#endif diff --git a/src/TypeInfo.cpp b/src/TypeInfo.cpp new file mode 100644 index 0000000..39b5fda --- /dev/null +++ b/src/TypeInfo.cpp @@ -0,0 +1,645 @@ +// $Id: TypeInfo.cpp,v 1.58 2002/04/20 06:11:32 cthuang Exp $ +#pragma warning(disable: 4786) +#include +#include +#include "Uuid.h" +#include "TypeInfo.h" + +std::string +getTypeInfoName (ITypeInfo *pTypeInfo, MEMBERID id) +{ + BSTR nameStr = 0; + HRESULT hResult = pTypeInfo->GetDocumentation( + id, &nameStr, NULL, NULL, NULL); + if (FAILED(hResult) || nameStr == 0) { + return std::string(); + } + _bstr_t wrapper(nameStr, false); + return std::string(wrapper); +} + + +struct VarTypeStringAssoc { + VARTYPE vt; + const char *name; +}; + +static VarTypeStringAssoc varTypeStringAssocs[] = { + { VT_EMPTY, "EMPTY" }, + { VT_NULL, "NULL" }, + { VT_I2, "I2" }, + { VT_I4, "I4" }, + { VT_R4, "R4" }, + { VT_R8, "R8" }, + { VT_CY, "CY" }, + { VT_DATE, "DATE" }, + { VT_BSTR, "BSTR" }, + { VT_DISPATCH, "DISPATCH" }, + { VT_ERROR, "ERROR" }, + { VT_BOOL, "BOOL" }, + { VT_VARIANT, "VARIANT" }, + { VT_UNKNOWN, "UNKNOWN" }, + { VT_DECIMAL, "DECIMAL" }, + { VT_RECORD, "RECORD" }, + { VT_I1, "I1" }, + { VT_UI1, "UI1" }, + { VT_UI2, "UI2" }, + { VT_UI4, "UI4" }, + { VT_INT, "INT" }, + { VT_UINT, "UINT" }, + { VT_VOID, "VOID" }, + { VT_LPSTR, "LPSTR" }, + { VT_LPWSTR, "LPWSTR" }, + { VT_ARRAY, "ARRAY" }, +}; + +// This class maps from a VARTYPE to a string representation. + +class VarTypeToStringMap: public std::map +{ +public: + VarTypeToStringMap(); +}; + +VarTypeToStringMap::VarTypeToStringMap () +{ + const int n = sizeof(varTypeStringAssocs) / sizeof(VarTypeStringAssoc); + for (int i = 0; i < n; ++i) { + const VarTypeStringAssoc &assoc = varTypeStringAssocs[i]; + insert(value_type(assoc.vt, assoc.name)); + } +} + +static VarTypeToStringMap varTypeToStringMap; + +// This class maps from a string representation to a VARTYPE. + +class StringToVarTypeMap: public std::map +{ +public: + StringToVarTypeMap(); +}; + +StringToVarTypeMap::StringToVarTypeMap () +{ + const int n = sizeof(varTypeStringAssocs) / sizeof(VarTypeStringAssoc); + for (int i = 0; i < n; ++i) { + const VarTypeStringAssoc &assoc = varTypeStringAssocs[i]; + insert(value_type(assoc.name, assoc.vt)); + } +} + +static StringToVarTypeMap stringToVarTypeMap; + +Type Type::ms_variant("VARIANT"); + +Type::Type (const ITypeInfoPtr &pTypeInfo, TYPEDESC &typeDesc): + m_pointerCount(0), + m_pElementType(0) +{ + UuidCreateNil(&m_iid); + readTypeDesc(pTypeInfo, typeDesc); +} + +Type::Type (const std::string &str): + m_pointerCount(0), + m_pElementType(0) +{ + UuidCreateNil(&m_iid); + + std::istringstream in(str); + std::string token; + while (in >> token) { + if (token[0] == '*') { + ++m_pointerCount; + } else { + StringToVarTypeMap::iterator p = stringToVarTypeMap.find(token); + if (p == stringToVarTypeMap.end()) { + m_vt = VT_USERDEFINED; + m_name = token; + } else { + m_vt = p->second; + } + } + } +} + +Type::Type (const Type &rhs): + m_name(rhs.m_name), + m_vt(rhs.m_vt), + m_iid(rhs.m_iid), + m_pointerCount(rhs.m_pointerCount), + m_pElementType(rhs.m_pElementType ? new Type(*rhs.m_pElementType) : 0) +{ } + +Type::~Type () +{ + delete m_pElementType; +} + +Type & +Type::operator= (const Type &rhs) +{ + m_name = rhs.m_name; + m_vt = rhs.m_vt; + m_iid = rhs.m_iid; + m_pointerCount = rhs.m_pointerCount; + + delete m_pElementType; + m_pElementType = rhs.m_pElementType ? new Type(*rhs.m_pElementType) : 0; + + return *this; +} + +std::string +Type::toString () const +{ + switch (m_vt) { + case VT_USERDEFINED: + return m_name; + + case VT_SAFEARRAY: + { + std::ostringstream out; + out << "SAFEARRAY(" << elementType().toString() << ")"; + return out.str(); + } + + default: + { + VarTypeToStringMap::iterator p = varTypeToStringMap.find(m_vt); + if (p != varTypeToStringMap.end()) { + return p->second; + } + + std::ostringstream out; + out << "vartype0x" << std::hex << m_vt; + return out.str(); + } + } +} + +void +Type::readTypeDesc (const ITypeInfoPtr &pTypeInfo, TYPEDESC &typeDesc) +{ + HRESULT hr; + + switch (typeDesc.vt) { + case VT_USERDEFINED: + { + // It's an alias. Expand the alias. + ITypeInfoPtr pRefTypeInfo; + hr = pTypeInfo->GetRefTypeInfo(typeDesc.hreftype, &pRefTypeInfo); + if (SUCCEEDED(hr)) { + if (m_name.empty()) { + m_name = getTypeInfoName(pRefTypeInfo); + } + + TypeAttr typeAttr(pRefTypeInfo); + if (typeAttr->typekind == TKIND_ALIAS) { + // Type expanded to another alias! + readTypeDesc(pRefTypeInfo, typeAttr->tdescAlias); + } else if (typeAttr->typekind == TKIND_ENUM) { + m_vt = VT_I4; + } else { + m_vt = typeDesc.vt; + m_iid = typeAttr->guid; + } + } + } + break; + + case VT_PTR: + // It's a pointer. Dereference and try to interpret with one less + // level of indirection. + ++m_pointerCount; + readTypeDesc(pTypeInfo, *typeDesc.lptdesc); + break; + + case VT_SAFEARRAY: + // It's a SAFEARRAY. Get the element type. + m_pElementType = new Type(pTypeInfo, *typeDesc.lptdesc); + m_vt = typeDesc.vt; + break; + + default: + m_vt = typeDesc.vt; + } +} + +Parameter::Parameter (const ITypeInfoPtr &pTypeInfo, + ELEMDESC &elemDesc, + const char *name): + m_flags(elemDesc.paramdesc.wParamFlags), + m_type(pTypeInfo, elemDesc.tdesc), + m_name(name) +{ + if (m_flags == 0) { + // No parameter passing flags are set. Assume it's an in parameter. + m_flags = PARAMFLAG_FIN; + } +} + +Parameter::Parameter (const std::string &modes, + const std::string &type, + const std::string &name): + m_flags(0), + m_type(type), + m_name(name) +{ + std::istringstream in(modes); + std::string token; + while (in >> token) { + if (token == "in") { + m_flags |= PARAMFLAG_FIN; + } else if (token == "out") { + m_flags |= PARAMFLAG_FOUT; + } + } +} + + +Method::Method (const ITypeInfoPtr &pTypeInfo, DISPID memberid, ELEMDESC &elemDesc): + m_memberid(memberid), + m_type(pTypeInfo, elemDesc.tdesc) +{ + // Get name. + BSTR nameBstr; + unsigned numNames; + HRESULT hr = pTypeInfo->GetNames( + memberid, + &nameBstr, + 1, + &numNames); + if (FAILED(hr)) { + // TODO: I should throw an exception here. + return; + } + + // Initialize name. + _bstr_t name(nameBstr, false); + m_name = std::string(name); +} + +Method::Method (const ITypeInfoPtr &pTypeInfo, FuncDesc &funcDesc): + m_memberid(funcDesc->memid), + m_type(pTypeInfo, funcDesc->elemdescFunc.tdesc), + m_invokeKind(funcDesc->invkind), + m_vtblIndex(funcDesc->oVft / 4), + m_vararg(funcDesc->cParamsOpt == -1) +{ + // Get method and parameter names. + BSTR *pNames = new BSTR[funcDesc->cParams + 1]; + unsigned numNames; + HRESULT hr = pTypeInfo->GetNames( + funcDesc->memid, + pNames, + funcDesc->cParams + 1, + &numNames); + if (FAILED(hr)) { + // TODO: I should throw an exception here. + delete[] pNames; + return; + } + + // Initialize method name. + _bstr_t methodName(pNames[0], false); + m_name = std::string(methodName); + + // Get parameter types. + for (unsigned i = 1; i < numNames; ++i) { + _bstr_t paramName(pNames[i], false); + Parameter parameter( + pTypeInfo, + funcDesc->lprgelemdescParam[i - 1], + paramName); + addParameter(parameter); + } + + // Some TKIND_INTERFACE methods specify a parameter is a return value + // with the PARAMFLAG_FRETVAL flag. Check if the last parameter is + // actually a return value. + if (m_type.vartype() == VT_HRESULT) { + m_type = Type("VOID"); + + if (m_parameters.size() > 0) { + Parameter last = m_parameters.back(); + if ((last.flags() & (PARAMFLAG_FOUT|PARAMFLAG_FRETVAL)) + == (PARAMFLAG_FOUT|PARAMFLAG_FRETVAL)) { + m_type = last.type(); + m_parameters.pop_back(); + } + } + } + + delete[] pNames; +} + +Method::Method (MEMBERID memberid, + const std::string &type, + const std::string &name, + INVOKEKIND invokeKind): + m_memberid(memberid), + m_type(type), + m_name(name), + m_invokeKind(invokeKind) +{ } + + +Property::Property (const ITypeInfoPtr &pTypeInfo, FuncDesc &funcDesc): + Method(pTypeInfo, funcDesc), + m_readOnly(true) +{ + initialize(); +} + +Property::Property (const ITypeInfoPtr &pTypeInfo, VarDesc &varDesc): + Method(pTypeInfo, varDesc->memid, varDesc->elemdescVar), + m_readOnly((varDesc->wVarFlags & VARFLAG_FREADONLY) != 0) +{ + initialize(); +} + +Property::Property (MEMBERID memberid, + const std::string &modes, + const std::string &type, + const std::string &name): + Method(memberid, type, name, INVOKE_PROPERTYGET), + m_readOnly(true) +{ + // Initialize readable/writable flags. + std::istringstream in(modes); + std::string token; + while (in >> token) { + if (token == "in") { + m_readOnly = false; + } + } +} + +void +Property::initialize () +{ + switch (invokeKind()) { + case INVOKE_PROPERTYPUT: + m_putDispatchFlag = DISPATCH_PROPERTYPUT; + break; + case INVOKE_PROPERTYPUTREF: + m_putDispatchFlag = DISPATCH_PROPERTYPUTREF; + break; + } +} + +void +Property::merge (const Property &property) +{ + // Set dispatch flag used for property put. + switch (property.invokeKind()) { + case INVOKE_PROPERTYPUT: + m_putDispatchFlag = DISPATCH_PROPERTYPUT; + break; + case INVOKE_PROPERTYPUTREF: + m_putDispatchFlag = DISPATCH_PROPERTYPUTREF; + break; + } + + // Set read only flag. + if (property.invokeKind() != INVOKE_PROPERTYGET) { + m_readOnly = false; + } + + // Set property value type. + if (property.type().vartype() != VT_VOID) { + type(property.type()); + } +} + + +std::string +Interface::iidString () const +{ + Uuid uuid(m_iid); + return uuid.toString(); +} + +void +Interface::addMethod (const Method &method) +{ + // Do we already have information on this method? + for (Methods::iterator p = m_methods.begin(); p != m_methods.end(); ++p) { + if (p->memberid() == method.memberid() + && p->vtblIndex() == method.vtblIndex()) { + return; + } + } + + // Add method description. + m_methods.push_back(method); +} + +void +Interface::addProperty (const Property &property) +{ + // Do we already have information on this property? + for (Properties::iterator p = m_properties.begin(); + p != m_properties.end(); ++p) { + if (p->memberid() == property.memberid()) { + p->merge(property); + return; + } + } + + // Add property description. + m_properties.push_back(property); +} + +void +Interface::readFunctions (const ITypeInfoPtr &pTypeInfo, TypeAttr &typeAttr) +{ + HRESULT hr; + + // Don't expose the IUnknown and IDispatch functions to the Tcl script + // because of potential dangers. + if (IsEqualIID(typeAttr->guid, IID_IUnknown) + || IsEqualIID(typeAttr->guid, IID_IDispatch)) { + return; + } + + // Get properties and methods from inherited interfaces. + for (int i = 0; i < typeAttr->cImplTypes; ++i) { + HREFTYPE hRefType; + hr = pTypeInfo->GetRefTypeOfImplType(i, &hRefType); + if (FAILED(hr)) { + break; + } + + ITypeInfoPtr pSuperTypeInfo; + hr = pTypeInfo->GetRefTypeInfo(hRefType, &pSuperTypeInfo); + if (FAILED(hr)) { + break; + } + TypeAttr superTypeAttr(pSuperTypeInfo); + readFunctions(pSuperTypeInfo, superTypeAttr); + } + + bool dual = (typeAttr->wTypeFlags & TYPEFLAG_FDUAL) != 0; + + // Get properties and methods of this interface. + for (int j = 0; j < typeAttr->cFuncs; ++j) { + FuncDesc funcDesc(pTypeInfo, j); + + if (dual && funcDesc->funckind == FUNC_DISPATCH + && funcDesc->oVft < 28) { + // Don't expose the IUnknown and IDispatch functions to the Tcl + // script because of potential dangers. + continue; + } + + Method method(pTypeInfo, funcDesc); + addMethod(method); + + if (funcDesc->invkind != INVOKE_FUNC) { + // This is a property get/set function. + Property property(pTypeInfo, funcDesc); + addProperty(property); + } + } + + // Some objects expose their properties as variable members. + for (int k = 0; k < typeAttr->cVars; ++k) { + VarDesc varDesc(pTypeInfo, k); + + if (varDesc->varkind == VAR_DISPATCH) { + Property property(pTypeInfo, varDesc); + addProperty(property); + } + } + + // Add missing parameter description to property put functions. + for (Methods::iterator pMethod = m_methods.begin(); + pMethod != m_methods.end(); ++pMethod) { + if (pMethod->invokeKind() == INVOKE_PROPERTYPUT + || pMethod->invokeKind() == INVOKE_PROPERTYPUTREF) { + const Property *pProperty = findProperty(pMethod->name().c_str()); + if (pProperty != 0) { + Parameter parameter( + PARAMFLAG_FIN, + pProperty->type(), + "propertyValue"); + pMethod->addParameter(parameter); + } + } + } +} + +void +Interface::readTypeInfo (const ITypeInfoPtr &pTypeInfo) +{ + m_name = getTypeInfoName(pTypeInfo); + + TypeAttr typeAttr(pTypeInfo); + m_iid = typeAttr->guid; + m_dispatchOnly = (typeAttr->typekind == TKIND_DISPATCH) && + ((typeAttr->wTypeFlags & TYPEFLAG_FDUAL) == 0); + m_dispatchable = (typeAttr->wTypeFlags & TYPEFLAG_FDISPATCHABLE) != 0; + + m_methods.reserve(typeAttr->cFuncs); + m_properties.reserve(typeAttr->cFuncs); + readFunctions(pTypeInfo, typeAttr); +} + +const Method * +Interface::findMethod (const char *name) const +{ + for (Methods::const_iterator p = m_methods.begin(); + p != m_methods.end(); ++p) { + if (p->name() == name) { + return &(*p); + } + } + return 0; +} + +const Property * +Interface::findProperty (const char *name) const +{ + for (Properties::const_iterator p = m_properties.begin(); + p != m_properties.end(); ++p) { + if (p->name() == name) { + return &(*p); + } + } + return 0; +} + + +Singleton InterfaceManager::ms_singleton; + +InterfaceManager & +InterfaceManager::instance () +{ + return ms_singleton.instance(); +} + +InterfaceManager::InterfaceManager () +{ +} + +InterfaceManager::~InterfaceManager () +{ + // Delete cached interface descriptions. + m_hashTable.forEach(Delete()); +} + +const Interface * +InterfaceManager::newInterface (REFIID iid, const ITypeInfoPtr &pTypeInfo) +{ + LOCK_MUTEX(m_mutex) + + Interface *pInterface = m_hashTable.find(iid); + if (pInterface == 0) { + pInterface = new Interface(pTypeInfo); + m_hashTable.insert(iid, pInterface); + } + return pInterface; +} + +Interface * +InterfaceManager::newInterface (REFIID iid, const char *name) +{ + LOCK_MUTEX(m_mutex) + + Interface *pInterface = m_hashTable.find(iid); + if (pInterface == 0) { + pInterface = new Interface(iid, name); + m_hashTable.insert(iid, pInterface); + } + return pInterface; +} + +const Interface * +InterfaceManager::find (REFIID iid) const +{ + LOCK_MUTEX(m_mutex) + + return m_hashTable.find(iid); +} + + +FuncDesc::FuncDesc (const ITypeInfoPtr &pTypeInfo, unsigned index): + m_pTypeInfo(pTypeInfo) +{ + HRESULT hr = m_pTypeInfo->GetFuncDesc(index, &m_pFuncDesc); + if (FAILED(hr)) { + _com_issue_error(hr); + } +} + +VarDesc::VarDesc (const ITypeInfoPtr &pTypeInfo, unsigned index): + m_pTypeInfo(pTypeInfo) +{ + HRESULT hr = m_pTypeInfo->GetVarDesc(index, &m_pVarDesc); + if (FAILED(hr)) { + _com_issue_error(hr); + } +} diff --git a/src/TypeInfo.h b/src/TypeInfo.h new file mode 100644 index 0000000..2794e1a --- /dev/null +++ b/src/TypeInfo.h @@ -0,0 +1,455 @@ +// $Id: TypeInfo.h,v 1.41 2002/04/20 06:11:32 cthuang Exp $ +#ifndef TYPEINFO_H +#define TYPEINFO_H + +#include +#include +#include +#include "tcomApi.h" +#include "Uuid.h" +#include "HashTable.h" +#include "Singleton.h" + +// Define smart pointer class named ITypeInfoPtr which automatically calls the +// IUnknown methods. +_COM_SMARTPTR_TYPEDEF(ITypeInfo, __uuidof(ITypeInfo)); + +// Get name of type described by the ITypeInfo object. +std::string getTypeInfoName(ITypeInfo *pTypeInfo, MEMBERID id=MEMBERID_NIL); + +// This wrapper class takes ownership of the resource and is responsible for +// releasing it. + +class TCOM_API TypeAttr +{ + ITypeInfoPtr m_pTypeInfo; + TYPEATTR *m_pTypeAttr; + + // Do not allow others to copy instances of this class. + TypeAttr(const TypeAttr &); // not implemented + void operator=(const TypeAttr &); // not implemented + +public: + // TODO: I should probably check for a failure result and throw an + // exception in that case. + TypeAttr (const ITypeInfoPtr &pTypeInfo): + m_pTypeInfo(pTypeInfo) + { m_pTypeInfo->GetTypeAttr(&m_pTypeAttr); } + + ~TypeAttr () + { m_pTypeInfo->ReleaseTypeAttr(m_pTypeAttr); } + + // Deference pointer. + TYPEATTR *operator-> () const + { return m_pTypeAttr; } +}; + +// This wrapper class takes ownership of the resource and is responsible for +// releasing it. + +class TCOM_API FuncDesc +{ + ITypeInfoPtr m_pTypeInfo; + FUNCDESC *m_pFuncDesc; + + // Do not allow others to copy instances of this class. + FuncDesc(const FuncDesc &); // not implemented + void operator=(const FuncDesc &); // not implemented + +public: + FuncDesc(const ITypeInfoPtr &pTypeInfo, unsigned index); + + ~FuncDesc () + { m_pTypeInfo->ReleaseFuncDesc(m_pFuncDesc); } + + // Dereference pointer. + FUNCDESC *operator-> () const + { return m_pFuncDesc; } +}; + +// This wrapper class takes ownership of the resource and is responsible for +// releasing it. + +class TCOM_API VarDesc +{ + ITypeInfoPtr m_pTypeInfo; + VARDESC *m_pVarDesc; + + // Do not allow others to copy instances of this class. + VarDesc(const VarDesc &); // not implemented + void operator=(const VarDesc &); // not implemented + +public: + VarDesc(const ITypeInfoPtr &pTypeInfo, unsigned index); + + ~VarDesc () + { m_pTypeInfo->ReleaseVarDesc(m_pVarDesc); } + + // Dereference pointer. + VARDESC *operator-> () const + { return m_pVarDesc; } +}; + +// This class describes a type for a function return value or parameter. + +class TCOM_API Type +{ + std::string m_name; + VARTYPE m_vt; + IID m_iid; + unsigned m_pointerCount; + Type *m_pElementType; // element type for arrays + + // description for VARIANT type + static Type ms_variant; + + void readTypeDesc(const ITypeInfoPtr &pTypeInfo, TYPEDESC &typeDesc); + +public: + Type(const ITypeInfoPtr &pTypeInfo, TYPEDESC &typeDesc); + Type(const std::string &str); + Type(const Type &rhs); + + ~Type(); + + Type &operator=(const Type &rhs); + + const std::string &name () const + { return m_name; } + + VARTYPE vartype () const + { return m_vt; } + + const IID &iid () const + { return m_iid; } + + unsigned pointerCount () const + { return m_pointerCount; } + + const Type &elementType () const + { return *m_pElementType; } + + // Get string representation. + std::string toString() const; + + // Get description for VARIANT type. + static Type &variant() + { return ms_variant; } +}; + +// This class describes a function parameter. + +class TCOM_API Parameter +{ + std::string m_name; + Type m_type; + unsigned m_flags; + +public: + Parameter(const ITypeInfoPtr &pTypeInfo, ELEMDESC &elemDesc, const char *name); + + Parameter( + const std::string &modes, + const std::string &type, + const std::string &name); + + Parameter (unsigned flags, const Type &type, const char *name): + m_flags(flags), + m_type(type), + m_name(name) + { } + + const std::string &name () const + { return m_name; } + + const Type &type () const + { return m_type; } + + unsigned flags () const + { return m_flags; } +}; + +// This class describes a method. + +class TCOM_API Method +{ +public: + typedef std::vector Parameters; + +private: + std::string m_name; + Type m_type; + Parameters m_parameters; + MEMBERID m_memberid; + INVOKEKIND m_invokeKind; + short m_vtblIndex; // position in virtual function table + bool m_vararg; // method accepts variable number of arguments + +protected: + Method(const ITypeInfoPtr &pTypeInfo, DISPID memberid, ELEMDESC &elemDesc); + +public: + Method(const ITypeInfoPtr &pTypeInfo, FuncDesc &funcDesc); + + Method( + MEMBERID memberid, + const std::string &type, + const std::string &name, + INVOKEKIND invokeKind=INVOKE_FUNC); + + // Get method name. + const std::string &name () const + { return m_name; } + + // Get return value type. + const Type &type () const + { return m_type; } + + // Set return value type. + void type (const Type &rhs) + { m_type = rhs; } + + // Insert parameter. + void addParameter (const Parameter ¶meter) + { m_parameters.push_back(parameter); } + + // Get parameters. + const Parameters ¶meters () const + { return m_parameters; } + + // Get member ID. + MEMBERID memberid () const + { return m_memberid; } + + // Get indicator whether this is a method or property. + INVOKEKIND invokeKind () const + { return m_invokeKind; } + + // Set indicator whether this is a method or property. + void invokeKind (INVOKEKIND invokeKind) + { m_invokeKind = invokeKind; } + + // Get position in virtual function table. + short vtblIndex () const + { return m_vtblIndex; } + + // Return true if the method accepts variable number of arguments. + bool vararg () const + { return m_vararg; } +}; + +// This class describes a property. + +class TCOM_API Property: public Method +{ + WORD m_putDispatchFlag; + bool m_readOnly; + + // Initialize data members. + void initialize(); + +public: + Property(const ITypeInfoPtr &pTypeInfo, FuncDesc &funcDesc); + Property(const ITypeInfoPtr &pTypeInfo, VarDesc &varDesc); + + Property( + MEMBERID memberid, + const std::string &modes, + const std::string &type, + const std::string &name); + + // Merge data from other property into this one. + void merge(const Property &property); + + // Get dispatch flag for setting property. + WORD putDispatchFlag () const + { return m_putDispatchFlag; } + + // Get read only flag. + bool readOnly () const + { return m_readOnly; } +}; + +// This describes an interface. + +class TCOM_API Interface +{ + // Make the following a friend so it can construct instances of this class. + friend class InterfaceManager; + +public: + typedef std::vector Methods; + typedef std::vector Properties; + +private: + ITypeInfoPtr m_pTypeInfo; + IID m_iid; + std::string m_name; + Methods m_methods; + Properties m_properties; + bool m_dispatchOnly; + bool m_dispatchable; + + // Get information on methods and properties. + void readFunctions(const ITypeInfoPtr &pTypeInfo, TypeAttr &typeAttr); + + // Get information on interface. + void readTypeInfo(const ITypeInfoPtr &pTypeInfo); + + // Constructors + Interface (const ITypeInfoPtr &pTypeInfo): + m_pTypeInfo(pTypeInfo) + { readTypeInfo(pTypeInfo); } + + Interface (REFIID iid, const char *name): + m_iid(iid), + m_name(name) + { } + +public: + // Get IID. + const IID &iid () const + { return m_iid; } + + // Get string representatin of IID. + std::string iidString() const; + + // Get name. + const std::string &name () const + { return m_name; } + + // Get type info description. + ITypeInfo *typeInfo () const + { return m_pTypeInfo.GetInterfacePtr(); } + + // Insert method information. + void addMethod(const Method &method); + + // Get methods. + const Methods &methods () const + { return m_methods; } + + // Find the named method. + const Method *findMethod(const char *name) const; + + // Insert property information. + void addProperty(const Property &property); + + // Get properties. + const Properties &properties () const + { return m_properties; } + + // Find the named property. + const Property *findProperty(const char *name) const; + + // Return true if this interface can only be invoked through IDispatch. + bool dispatchOnly () const + { return m_dispatchOnly; } + + // Return true if this interface derives from IDispatch. + bool dispatchable () const + { return m_dispatchable; } +}; + +// This is a cache of interface descriptions. + +class TCOM_API InterfaceManager +{ + // used to synchronize access to hash table + Mutex m_mutex; + + // IID to interface description map + typedef HashTable IidToInterfaceDescMap; + IidToInterfaceDescMap m_hashTable; + + friend class Singleton; + static Singleton ms_singleton; + + // Do not allow others to create or copy instances of this class. + InterfaceManager(); + ~InterfaceManager(); + InterfaceManager(const InterfaceManager &); // not implemented + void operator=(const InterfaceManager &); // not implemented + +public: + // Get singleton instance. + static InterfaceManager &instance(); + + // If the interface description already exists, return it, + // otherwise create a new interface description. + const Interface *newInterface(REFIID iid, const ITypeInfoPtr &pTypeInfo); + + // If the interface description already exists, return it, + // otherwise create a new interface description. + Interface *newInterface(REFIID iid, const char *name); + + // Look for the interface description. + const Interface *find(REFIID iid) const; +}; + +// This adapts an interface description so we can create a handle for it. +// We need this because the handle support classes take ownership of the +// application objects passed to them and will delete them, but the +// InterfaceManager maintains the life cycle of interface descriptions. + +class TCOM_API InterfaceHolder +{ + const Interface *m_pInterface; + +public: + InterfaceHolder (const Interface *pInterface): + m_pInterface(pInterface) + { } + + const Interface *interfaceDesc () const + { return m_pInterface; } +}; + +// This describes a class. + +class Class +{ +public: + typedef std::vector Interfaces; + +private: + std::string m_name; + CLSID m_clsid; + Interfaces m_interfaces; + const Interface *m_pDefaultInterface; + const Interface *m_pSourceInterface; + +public: + Class(const ITypeInfoPtr &pTypeInfo); + Class( + const char *name, + const CLSID &clsid, + const Interface *pDefaultInterface, + const Interface *pSourceInterface); + + // Get name. + const std::string &name () const + { return m_name; } + + // Get CLSID. + const CLSID &clsid () const + { return m_clsid; } + + // Get CLSID as string. + std::string clsidString () const + { Uuid uuid(m_clsid); return uuid.toString(); } + + // Get interfaces this class implements. + const Interfaces &interfaces () const + { return m_interfaces; } + + // Get default interface. + const Interface *defaultInterface () const + { return m_pDefaultInterface; } + + // Get default source interface. + const Interface *sourceInterface () const + { return m_pSourceInterface; } +}; + +#endif diff --git a/src/TypeLib.cpp b/src/TypeLib.cpp new file mode 100644 index 0000000..37f4591 --- /dev/null +++ b/src/TypeLib.cpp @@ -0,0 +1,366 @@ +// $Id: TypeLib.cpp,v 1.29 2002/03/09 16:40:24 cthuang Exp $ +#pragma warning(disable: 4786) +#include +#include "RegistryKey.h" +#include "TypeInfo.h" +#include "TypeLib.h" + +Class::Class (const ITypeInfoPtr &pTypeInfo): + m_name(getTypeInfoName(pTypeInfo)), + m_pDefaultInterface(0), + m_pSourceInterface(0) +{ + HRESULT hr; + + TypeAttr typeAttr(pTypeInfo); + m_clsid = typeAttr->guid; + + // Get interfaces this class implements. + unsigned interfaceCount = static_cast(typeAttr->cImplTypes); + for (unsigned i = 0; i < interfaceCount; ++i) { + HREFTYPE hRefType; + hr = pTypeInfo->GetRefTypeOfImplType(i, &hRefType); + if (FAILED(hr)) { + break; + } + + ITypeInfoPtr pInterfaceTypeInfo; + hr = pTypeInfo->GetRefTypeInfo(hRefType, &pInterfaceTypeInfo); + if (FAILED(hr)) { + break; + } + TypeAttr interfaceTypeAttr(pInterfaceTypeInfo); + + const Interface *pInterface = InterfaceManager::instance().newInterface( + interfaceTypeAttr->guid, pInterfaceTypeInfo); + + int flags; + hr = pTypeInfo->GetImplTypeFlags(i, &flags); + if (FAILED(hr)) { + break; + } + flags &= (IMPLTYPEFLAG_FDEFAULT | IMPLTYPEFLAG_FSOURCE); + + if (flags == IMPLTYPEFLAG_FDEFAULT) { + m_pDefaultInterface = pInterface; + } else if (flags == (IMPLTYPEFLAG_FDEFAULT | IMPLTYPEFLAG_FSOURCE)) { + m_pSourceInterface = pInterface; + } + + if ((flags & IMPLTYPEFLAG_FSOURCE) == 0) { + if (m_pDefaultInterface == 0) { + m_pDefaultInterface = pInterface; + } + m_interfaces.push_back(pInterface); + } + } +} + +Class::Class ( + const char *name, + REFCLSID clsid, + const Interface *pDefaultInterface, + const Interface *pSourceInterface): + m_name(name), + m_clsid(clsid), + m_pDefaultInterface(pDefaultInterface), + m_pSourceInterface(pSourceInterface) +{ + m_interfaces.push_back(pDefaultInterface); +} + + +Enum::Enum (const ITypeInfoPtr &pTypeInfo, TypeAttr &attr): + m_name(getTypeInfoName(pTypeInfo)) +{ + HRESULT hr; + + for (int iEnum = 0; iEnum < attr->cVars; ++iEnum) { + // Get enumerator description. + VARDESC *pVarDesc; + hr = pTypeInfo->GetVarDesc(iEnum, &pVarDesc); + if (FAILED(hr)) { + break; + } + + // Get enumerator name. + BSTR bstrName; + UINT namesReturned; + hr = pTypeInfo->GetNames(pVarDesc->memid, &bstrName, 1, &namesReturned); + if (SUCCEEDED(hr)) { + // Remember enumerator name and value. + _bstr_t enumNameBstr(bstrName); + std::string enumName(enumNameBstr); + + _variant_t enumValueVar(pVarDesc->lpvarValue); + _bstr_t enumValueBstr(enumValueVar); + std::string enumValue(enumValueBstr); + + insert(value_type(enumName, enumValue)); + } + + pTypeInfo->ReleaseVarDesc(pVarDesc); + } +} + + +TypeLib * +TypeLib::load (const char *name, bool registerTypeLib) +{ + _bstr_t nameStr(name); + REGKIND regKind = registerTypeLib ? REGKIND_REGISTER : REGKIND_NONE; + ITypeLibPtr pTypeLib; + HRESULT hr = LoadTypeLibEx(nameStr, regKind, &pTypeLib); + if (FAILED(hr)) { + _com_issue_error(hr); + } + return new TypeLib(pTypeLib); +} + +TypeLib * +TypeLib::loadByLibid (const std::string &libidStr, const std::string &version) +{ + // Remove braces so UuidFromString does not complain. + std::string cleanLibid; + if (libidStr[0] == '{') { + cleanLibid = libidStr.substr(1, libidStr.size() - 2); + } else { + cleanLibid = libidStr; + } + + IID libid; + if (UuidFromString( + reinterpret_cast(const_cast(cleanLibid.c_str())), + &libid) != RPC_S_OK) { + return 0; + } + + std::string::size_type i = version.find('.'); + std::istringstream majorIn(version.substr(0, i)); + unsigned short majorVersion; + majorIn >> majorVersion; + + unsigned short minorVersion = 0; + if (i != std::string::npos) { + std::istringstream minorIn(version.substr(i + 1)); + minorIn >> minorVersion; + } + + ITypeLibPtr pTypeLib; + HRESULT hr = LoadRegTypeLib( + libid, majorVersion, minorVersion, LOCALE_USER_DEFAULT, &pTypeLib); + if (FAILED(hr)) { + _com_issue_error(hr); + } + return new TypeLib(pTypeLib); +} + +TypeLib * +TypeLib::loadByClsid (REFCLSID clsid) +{ + std::string libidStr, version; + try { + // Get the LIBID of the type library for the class. + std::string clsidSubkeyName("CLSID\\{"); + Uuid uuid(clsid); + clsidSubkeyName.append(uuid.toString()); + clsidSubkeyName.append("}"); + + std::string typeLibSubkeyName = + clsidSubkeyName + "\\TypeLib"; + RegistryKey typeLibKey(HKEY_CLASSES_ROOT, typeLibSubkeyName); + libidStr = typeLibKey.value(); + + std::string versionSubkeyName = + clsidSubkeyName + "\\Version"; + RegistryKey versionKey(HKEY_CLASSES_ROOT, versionSubkeyName); + version = versionKey.value(); + } + catch (std::runtime_error &) { + return 0; + } + return loadByLibid(libidStr, version); +} + +TypeLib * +TypeLib::loadByIid (REFIID iid) +{ + std::string libidStr, version; + try { + // Get the LIBID of the type library for the interface. + std::string typeLibSubkeyName("Interface\\{"); + Uuid uuid(iid); + typeLibSubkeyName.append(uuid.toString()); + typeLibSubkeyName.append("}\\TypeLib"); + + RegistryKey typeLibKey(HKEY_CLASSES_ROOT, typeLibSubkeyName); + libidStr = typeLibKey.value(); + version = typeLibKey.value("Version"); + } + catch (std::runtime_error &) { + return 0; + } + return loadByLibid(libidStr, version); +} + +void +TypeLib::unregister (const char *name) +{ + HRESULT hr; + + ITypeLibPtr pTypeLib; + _bstr_t nameStr(name); + hr = LoadTypeLibEx(nameStr, REGKIND_NONE, &pTypeLib); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + TypeLibAttr pLibAttr(pTypeLib); + + hr = UnRegisterTypeLib( + pLibAttr->guid, + pLibAttr->wMajorVerNum, + pLibAttr->wMinorVerNum, + LANG_NEUTRAL, + SYS_WIN32); + if (FAILED(hr)) { + _com_issue_error(hr); + } +} + +std::string +TypeLib::libidString () const +{ + TypeLibAttr pLibAttr(m_pTypeLib); + + Uuid uuid(pLibAttr->guid); + return uuid.toString(); +} + +std::string +TypeLib::version () const +{ + TypeLibAttr pLibAttr(m_pTypeLib); + + std::ostringstream out; + out << pLibAttr->wMajorVerNum << '.' << pLibAttr->wMinorVerNum; + return out.str(); +} + +std::string +TypeLib::name () const +{ + BSTR nameStr; + HRESULT hr = m_pTypeLib->GetDocumentation( + MEMBERID_NIL, &nameStr, NULL, NULL, NULL); + if (FAILED(hr)) { + return std::string(); + } + _bstr_t wrapper(nameStr, false); + return std::string(wrapper); +} + +std::string +TypeLib::documentation () const +{ + BSTR docStr; + HRESULT hr = m_pTypeLib->GetDocumentation( + MEMBERID_NIL, NULL, &docStr, NULL, NULL); + if (FAILED(hr)) { + return std::string(); + } + _bstr_t wrapper(docStr, false); + return std::string(wrapper); +} + +const Interface * +TypeLib::findInterface (const char *name) const +{ + for (Interfaces::const_iterator p = m_interfaces.begin(); + p != m_interfaces.end(); ++p) { + if ((*p)->name() == name) { + return *p; + } + } + return 0; +} + +const Class * +TypeLib::findClass (const char *name) const +{ + for (Classes::const_iterator p = m_classes.begin(); + p != m_classes.end(); ++p) { + if (p->name() == name) { + return &(*p); + } + } + return 0; +} + +const Class * +TypeLib::findClass (REFCLSID clsid) const +{ + for (Classes::const_iterator p = m_classes.begin(); + p != m_classes.end(); ++p) { + if (IsEqualCLSID(p->clsid(), clsid)) { + return &(*p); + } + } + return 0; +} + +const Enum * +TypeLib::findEnum (const char *name) const +{ + for (Enums::const_iterator p = m_enums.begin(); p != m_enums.end(); ++p) { + if (p->name() == name) { + return &(*p); + } + } + return 0; +} + +void +TypeLib::readTypeLib () +{ + HRESULT hResult; + + unsigned count = m_pTypeLib->GetTypeInfoCount(); + for (unsigned index = 0; index < count; ++index) { + ITypeInfoPtr pTypeInfo; + hResult = m_pTypeLib->GetTypeInfo(index, &pTypeInfo); + if (FAILED(hResult)) { + continue; + } + TypeAttr typeAttr(pTypeInfo); + + switch (typeAttr->typekind) { + case TKIND_DISPATCH: + case TKIND_INTERFACE: + // Read interface description. + { + const Interface *pInterface = + InterfaceManager::instance().newInterface( + typeAttr->guid, pTypeInfo); + m_interfaces.push_back(pInterface); + } + break; + + case TKIND_COCLASS: + // Read class description. + { + Class aClass(pTypeInfo); + m_classes.push_back(aClass); + } + break; + + case TKIND_ENUM: + // Read the enumeration values. + { + Enum anEnum(pTypeInfo, typeAttr); + m_enums.push_back(anEnum); + } + break; + } + } +} diff --git a/src/TypeLib.h b/src/TypeLib.h new file mode 100644 index 0000000..8d989a1 --- /dev/null +++ b/src/TypeLib.h @@ -0,0 +1,140 @@ +// $Id: TypeLib.h,v 1.21 2002/03/09 16:40:24 cthuang Exp $ +#ifndef TYPELIB_H +#define TYPELIB_H + +#include +#include +#include +#include "TypeInfo.h" + +// This describes an enumeration with a map where +// the key is an enumerator name and +// the data is the enumerator value. + +class Enum: public std::map +{ + std::string m_name; + +public: + Enum(const ITypeInfoPtr &pTypeInfo, TypeAttr &attr); + + // Get name. + const std::string &name () const + { return m_name; } +}; + +// Define smart pointer class named ITypeLibPtr which automatically calls the +// IUnknown methods. +_COM_SMARTPTR_TYPEDEF(ITypeLib, __uuidof(ITypeLib)); + +// This wrapper class takes ownership of the resource and is responsible for +// releasing it. + +class TCOM_API TypeLibAttr +{ + ITypeLibPtr m_pTypeLib; + TLIBATTR *m_pLibAttr; + + // Do not allow others to copy instances of this class. + TypeLibAttr(const TypeLibAttr &); // not implemented + void operator=(const TypeLibAttr &); // not implemented + +public: + // TODO: I should probably check for a failure result and throw an + // exception in that case. + TypeLibAttr (const ITypeLibPtr &pTypeLib): + m_pTypeLib(pTypeLib) + { m_pTypeLib->GetLibAttr(&m_pLibAttr); } + + ~TypeLibAttr () + { m_pTypeLib->ReleaseTLibAttr(m_pLibAttr); } + + // Deference pointer. + TLIBATTR *operator-> () const + { return m_pLibAttr; } +}; + +// This class represents a type library. + +class TypeLib +{ +public: + typedef std::vector Interfaces; + typedef std::vector Classes; + typedef std::vector Enums; + +private: + ITypeLibPtr m_pTypeLib; + Interfaces m_interfaces; + Classes m_classes; + Enums m_enums; + + TypeLib (const ITypeLibPtr &pTypeLib): + m_pTypeLib(pTypeLib) + { readTypeLib(); } + + // Do not allow others to copy instances of this class. + TypeLib(const TypeLib &); // not implemented + void operator=(const TypeLib &); // not implemented + + // Get information from type library. + void readTypeLib(); + +public: + // Load a type library from the specified file. + static TypeLib *load(const char *name, bool registerTypeLib=false); + + // Unregister a type library. + static void unregister(const char *name); + + // Load a type library specified by a LIBID. + // Return 0 if the required registry entries were not found. + static TypeLib *loadByLibid( + const std::string &libid, const std::string &version); + + // Load a type library specified by a CLSID. + // Return 0 if the required registry entries were not found. + static TypeLib *loadByClsid(REFCLSID clsid); + + // Load a type library specified by an IID. + // Return 0 if the required registry entries were not found. + static TypeLib *loadByIid(REFIID iid); + + // Get string representation of type library ID. + std::string libidString() const; + + // Get type library version. + std::string version() const; + + // Get type library name. + std::string name() const; + + // Get type library documentation string. + std::string documentation() const; + + // Get interfaces. + const Interfaces &interfaces () const + { return m_interfaces; } + + // Get the named interface. + const Interface *findInterface(const char *name) const; + + // Get classes. + const Classes &classes () const + { return m_classes; } + + // Get the named class. + const Class *findClass(const char *name) const; + + // Find class by CLSID. + const Class *findClass(REFCLSID clsid) const; + + // Get enumerations. + const Enums &enums () const + { return m_enums; } + + // Get the named enumeration. + const Enum *findEnum(const char *name) const; +}; + +#endif diff --git a/src/Uuid.cpp b/src/Uuid.cpp new file mode 100644 index 0000000..a45e740 --- /dev/null +++ b/src/Uuid.cpp @@ -0,0 +1,14 @@ +// $Id: Uuid.cpp,v 1.2 2000/04/20 18:37:40 chuang Exp $ +#include "Uuid.h" + +std::string +Uuid::toString () const +{ + unsigned char *str; + if (UuidToString(const_cast(&m_uuid), &str) != RPC_S_OK) { + return std::string(); + } + std::string result(reinterpret_cast(str)); + RpcStringFree(&str); + return result; +} diff --git a/src/Uuid.h b/src/Uuid.h new file mode 100644 index 0000000..ab01674 --- /dev/null +++ b/src/Uuid.h @@ -0,0 +1,34 @@ +// $Id: Uuid.h,v 1.3 2000/04/28 19:37:53 chuang Exp $ +#ifndef UUID_H +#define UUID_H + +#include +#include +#include +#include "tcomApi.h" + +// This class wraps a UUID to provide convenience functions. + +class TCOM_API Uuid +{ + UUID m_uuid; + +public: + // Construct from UUID. + Uuid (const UUID &uuid): + m_uuid(uuid) + { } + + // less than operator + bool operator< (const Uuid &rhs) const + { return memcmp(&m_uuid, &rhs.m_uuid, sizeof(UUID)) < 0; } + + // equals operator + bool operator== (const Uuid &rhs) const + { return memcmp(&m_uuid, &rhs.m_uuid, sizeof(UUID)) == 0; } + + // Return string representation. + std::string toString() const; +}; + +#endif diff --git a/src/bindCmd.cpp b/src/bindCmd.cpp new file mode 100644 index 0000000..a336015 --- /dev/null +++ b/src/bindCmd.cpp @@ -0,0 +1,238 @@ +// $Id: bindCmd.cpp,v 1.52 2002/04/13 03:53:56 cthuang Exp $ +#pragma warning(disable: 4786) +#include "Extension.h" +#include "Reference.h" +#include "TypeLib.h" + +// Get the interface description for the specified IID. +// On failure, put a message in the Tcl interpreter result and return 0. + +static const Interface * +getInterfaceDesc (Tcl_Interp *interp, REFIID iid) +{ + const Interface *pInterface = InterfaceManager::instance().find(iid); + if (pInterface == 0) { + Tcl_AppendResult( + interp, "no event interface information", NULL); + } + return pInterface; +} + +// Get the default source interface from the class description provided by +// IProvideClassInfo. +// On failure, return 0. + +static const Interface * +findEventInterfaceFromProvideClassInfo (IUnknown *pObject) +{ + HRESULT hr; + + IProvideClassInfoPtr pProvideClassInfo; + hr = pObject->QueryInterface( + IID_IProvideClassInfo, + reinterpret_cast(&pProvideClassInfo)); + if (FAILED(hr)) { + return 0; + } + + ITypeInfoPtr pClassTypeInfo; + hr = pProvideClassInfo->GetClassInfo(&pClassTypeInfo); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + // Get default source interface from class description. + Class aClass(pClassTypeInfo); + return aClass.sourceInterface(); +} + +// Get the default source interface from the class description loaded from a +// type library specified by a CLSID. +// On failure, return 0. + +static const Interface * +findEventInterfaceFromClsid (Reference *pReference) +{ + const Class *pClass = pReference->classDesc(); + if (pClass == 0) { + return 0; + } + return pClass->sourceInterface(); +} + +// Get the event interface managed by the first connection point from the +// connection point container. +// On failure, put a message in the Tcl interpreter result and return 0. + +static const Interface * +findEventInterfaceFromConnectionPoint (Tcl_Interp *interp, IUnknown *pObject) +{ + HRESULT hr; + + // Get connection point container. + IConnectionPointContainerPtr pContainer; + hr = pObject->QueryInterface( + IID_IConnectionPointContainer, + reinterpret_cast(&pContainer)); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + // Get connection point enumerator. + IEnumConnectionPointsPtr pEnum; + hr = pContainer->EnumConnectionPoints(&pEnum); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + // Get first connection point. + IConnectionPointPtr pConnectionPoint; + ULONG count; + hr = pEnum->Next(1, &pConnectionPoint, &count); + if (FAILED(hr)) { + _com_issue_error(hr); + } + if (count == 0) { + Tcl_AppendResult( + interp, "IEnumConnectionPoints returned no elements", NULL); + return 0; + } + + // Get IID of event interface managed by the connection point. + IID iid; + hr = pConnectionPoint->GetConnectionInterface(&iid); + if (FAILED(hr)) { + _com_issue_error(hr); + } + + // Get interface description for the IID. + const Interface *pInterface = InterfaceManager::instance().find(iid); + if (pInterface != 0) { + return pInterface; + } + + // If we don't have the interface description in the cache, try loading + // it from the type library. + TypeLib *pTypeLib = TypeLib::loadByIid(iid); + delete pTypeLib; + return getInterfaceDesc(interp, iid); +} + +// Find the event interface. +// On failure, put a message in the Tcl interpreter result and return 0. + +static const Interface * +findEventInterface ( + Tcl_Interp *interp, + Reference *pReference, + char *eventIIDStr) +{ + if (eventIIDStr != 0) { + // The script provided the IID of the event interface. + IID eventIID; + if (UuidFromString(reinterpret_cast(eventIIDStr), + &eventIID) != RPC_S_OK) { + Tcl_AppendResult( + interp, + "cannot convert to IID: ", + eventIIDStr, + NULL); + return 0; + } + + return getInterfaceDesc(interp, eventIID); + } + + const Interface *pInterface; + + // If the object implements IProvideClassInfo, get the default source + // interface from the class description. + pInterface = findEventInterfaceFromProvideClassInfo(pReference->unknown()); + if (pInterface != 0) { + return pInterface; + } + + // If we know the CLSID of the object's class, load the type library + // containing the class description, and get the default source interface + // from the class description. + pInterface = findEventInterfaceFromClsid(pReference); + if (pInterface != 0) { + return pInterface; + } + + // Get the event interface of the first connection point in the connection + // pointer container. + return findEventInterfaceFromConnectionPoint(interp, pReference->unknown()); +} + +// This Tcl command binds a Tcl command to an event sink. + +int +Extension::bindCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 3 || objc > 4) { + Tcl_WrongNumArgs(interp, 1, objv, "object sinkCommand ?eventIID?"); + return TCL_ERROR; + } + + Reference *pReference = referenceHandles.find(interp, objv[1]); + if (pReference == 0) { + const char *arg = Tcl_GetStringFromObj(objv[1], 0); + Tcl_AppendResult( + interp, "invalid interface pointer handle ", arg, NULL); + return TCL_ERROR; + } + + TclObject servant(objv[2]); + + char *eventIIDStr = (objc < 4) ? 0 : Tcl_GetStringFromObj(objv[3], 0); + + try { + const Interface *pEventInterface = findEventInterface( + interp, pReference, eventIIDStr); + if (pEventInterface == 0) { + return TCL_ERROR; + } + + pReference->advise(interp, *pEventInterface, servant); + } + catch (_com_error &e) { + return setComErrorResult(interp, e, __FILE__, __LINE__); + } + return TCL_OK; +} + +// This Tcl command tears down all event connections to the object. + +int +Extension::unbindCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "object"); + return TCL_ERROR; + } + + Reference *pReference = referenceHandles.find(interp, objv[1]); + if (pReference == 0) { + const char *arg = Tcl_GetStringFromObj(objv[1], 0); + Tcl_AppendResult( + interp, "invalid interface pointer handle ", arg, NULL); + return TCL_ERROR; + } + + try { + pReference->unadvise(); + } + catch (_com_error &e) { + return setComErrorResult(interp, e, __FILE__, __LINE__); + } + return TCL_OK; +} diff --git a/src/buildNumber.h b/src/buildNumber.h new file mode 100644 index 0000000..dd59692 --- /dev/null +++ b/src/buildNumber.h @@ -0,0 +1 @@ +#define BUILD_NUMBER 13 diff --git a/src/comsupp.cpp b/src/comsupp.cpp new file mode 100644 index 0000000..a4128f5 --- /dev/null +++ b/src/comsupp.cpp @@ -0,0 +1,60 @@ +// $Id: comsupp.cpp,v 1.2 2001/07/12 04:09:58 cthuang Exp $ +// +// These functions are defined in comsupp.lib but Borland C++ does not include +// that library in its distribution, so we implement them here when compiling +// with Borland C++. +#include +#include +#include +#include + +// This value represents a missing optional parameter. +_variant_t vtMissing(DISP_E_PARAMNOTFOUND, VT_ERROR); + +// COM error handling routine + +void __stdcall +_com_issue_error (HRESULT hr) throw(_com_error) +{ + throw _com_error(hr); +} + +namespace _com_util { + +// Convert char * to BSTR + +BSTR __stdcall +ConvertStringToBSTR (const char* pSrc) throw(_com_error) +{ + if (pSrc == 0) { + return SysAllocString(0); + } + + // Guess the number of wide characters needed. + size_t destLen = strlen(pSrc) + 1; + wchar_t *pDest = new wchar_t[destLen]; + mbstowcs(pDest, pSrc, destLen); + BSTR result = SysAllocString(pDest); + delete[] pDest; + return result; +} + +// Convert BSTR to char * + +char* __stdcall +ConvertBSTRToString (BSTR pSrc) throw(_com_error) +{ + if (pSrc == 0) { + char *pDest = new char[1]; + *pDest = '\0'; + return pDest; + } + + // Guess the number of bytes needed. + size_t destLen = wcslen(pSrc) * 3 + 1; + char *pDest = new char[destLen]; + wcstombs(pDest, pSrc, destLen); + return pDest; +} + +} //namespace diff --git a/src/configureCmd.cpp b/src/configureCmd.cpp new file mode 100644 index 0000000..7e8ee1c --- /dev/null +++ b/src/configureCmd.cpp @@ -0,0 +1,95 @@ +// $Id: configureCmd.cpp,v 1.7 2002/04/13 03:53:57 cthuang Exp $ +#pragma warning(disable: 4786) +#include "Extension.h" + +// This Tcl command sets and retrieves configuration options. + +int +Extension::configureCmd ( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 2) { + Tcl_WrongNumArgs( + interp, 1, objv, "?optionName? ?value? ?optionName value? ..."); + return TCL_ERROR; + } + + Extension *pExtension = + static_cast(clientData); + + static char *options[] = { + "-concurrency", NULL + }; + enum OptionEnum { + CONCURRENCY + }; + + int index; + if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, + &index) != TCL_OK) { + return TCL_ERROR; + } + + switch (index) { + case CONCURRENCY: + if (objc == 2) { + // Get concurrency model. + char *result; + switch (pExtension->concurrencyModel()) { + case COINIT_APARTMENTTHREADED: + result = "apartmentthreaded"; + break; +#ifdef _WIN32_DCOM + case COINIT_MULTITHREADED: +#else + case 0: +#endif + result = "multithreaded"; + break; + default: + result = "unknown"; + } + Tcl_AppendResult(interp, result, NULL); + + } else if (objc == 3) { + // Set concurrency model. + static char *options[] = { + "apartmentthreaded", "multithreaded", NULL + }; + enum OptionEnum { + APARTMENTTHREADED, MULTITHREADED + }; + + int index; + if (Tcl_GetIndexFromObj(interp, objv[2], options, "concurrency", 0, + &index) != TCL_OK) { + return TCL_ERROR; + } + + DWORD flags; + switch (index) { + case APARTMENTTHREADED: + flags = COINIT_APARTMENTTHREADED; + break; + case MULTITHREADED: +#ifdef _WIN32_DCOM + flags = COINIT_MULTITHREADED; +#else + flags = 0; +#endif + break; + } + pExtension->concurrencyModel(flags); + + } else { + Tcl_WrongNumArgs( + interp, 2, objv, "apartmentthreaded|multithreaded"); + return TCL_ERROR; + } + return TCL_OK; + } + return TCL_ERROR; +} diff --git a/src/dllmain.cpp b/src/dllmain.cpp new file mode 100644 index 0000000..537d1c1 --- /dev/null +++ b/src/dllmain.cpp @@ -0,0 +1,81 @@ +// $Id: dllmain.cpp,v 1.16 2002/07/14 18:42:57 cthuang Exp $ +#pragma warning(disable: 4786) +#include "Uuid.h" +#include "HandleSupport.h" +#include "TclModule.h" +#include "TclInterp.h" +#include "tclRunTime.h" + +// This class implements a COM module for DLL-based servers. + +class DllModule: public TclModule +{ +public: + DllModule () + { } + + virtual void initializeCom(DWORD coinitFlags); +}; + +static DllModule module; + +void +DllModule::initializeCom (DWORD /*coinitFlags*/) +{ + // Do nothing. In-process servers should not call CoInitializeEx. +} + + +STDAPI +DllCanUnloadNow () +{ + return (module.lockCount() == 0) ? S_OK : S_FALSE; +} + +STDAPI +DllGetClassObject (REFCLSID clsid, REFIID iid, void **ppv) +{ + try { + IClassFactory *pFactory = module.find(clsid); + if (pFactory == 0) { + // Use CLSID to find initialize script from registry. + std::string clsidStr("{"); + Uuid uuid(clsid); + clsidStr += uuid.toString(); + clsidStr += "}"; + + int completionCode = module.registerFactoryByScript(clsidStr); + if (completionCode != TCL_OK) { + *ppv = 0; + return E_UNEXPECTED; + } + + pFactory = module.find(clsid); + } + + if (pFactory == 0) { + *ppv = 0; + return CLASS_E_CLASSNOTAVAILABLE; + } + return pFactory->QueryInterface(iid, ppv); + } + catch (...) { + *ppv = 0; + return CLASS_E_CLASSNOTAVAILABLE; + } +} + +BOOL WINAPI +DllMain ( + HINSTANCE hinstDLL, // handle to the DLL module + DWORD reason, // reason for calling function + LPVOID reserved) // reserved +{ + switch (reason) { + case DLL_PROCESS_DETACH: + module.terminate(); + break; + } + + return TRUE; +} diff --git a/src/dllserver.def b/src/dllserver.def new file mode 100644 index 0000000..ac20831 --- /dev/null +++ b/src/dllserver.def @@ -0,0 +1,5 @@ +LIBRARY tcominproc.dll + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE diff --git a/src/dllserver.dsp b/src/dllserver.dsp new file mode 100644 index 0000000..34ad259 --- /dev/null +++ b/src/dllserver.dsp @@ -0,0 +1,139 @@ +# Microsoft Developer Studio Project File - Name="dllserver" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=dllserver - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dllserver.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dllserver.mak" CFG="dllserver - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dllserver - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "dllserver - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dllserver - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "dllserver___Win32_Release" +# PROP BASE Intermediate_Dir "dllserver___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "dllserver_Release" +# PROP Intermediate_Dir "dllserver_Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DLLSERVER_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /Od /I "\tcl\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DLLSERVER_EXPORTS" /D "TCL_THREADS" /D "USE_TCL_STUBS" /D "USE_NON_CONST" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 Release\tcom.lib rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"dllserver_Release/tcominproc.dll" /libpath:"\tcl\lib" + +!ELSEIF "$(CFG)" == "dllserver - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "dllserver___Win32_Debug" +# PROP BASE Intermediate_Dir "dllserver___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "dllserver_Debug" +# PROP Intermediate_Dir "dllserver_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DLLSERVER_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "\tcl\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DLLSERVER_EXPORTS" /D "TCL_THREADS" /D "USE_TCL_STUBS" /D "USE_NON_CONST" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 Debug\tcom.lib rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"dllserver_Debug/tcominproc.dll" /libpath:"\tcl\lib" + +!ENDIF + +# Begin Target + +# Name "dllserver - Win32 Release" +# Name "dllserver - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\dllmain.cpp +# End Source File +# Begin Source File + +SOURCE=.\dllserver.def +# End Source File +# Begin Source File + +SOURCE=.\dllserverVersion.rc +# End Source File +# Begin Source File + +SOURCE=.\RegistryKey.cpp +# End Source File +# Begin Source File + +SOURCE=.\TclInterp.cpp +# End Source File +# Begin Source File + +SOURCE=.\TclModule.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\RegistryKey.h +# End Source File +# Begin Source File + +SOURCE=.\TclInterp.h +# End Source File +# Begin Source File + +SOURCE=.\TclModule.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/src/dllserverVersion.rc b/src/dllserverVersion.rc new file mode 100644 index 0000000..484be11 --- /dev/null +++ b/src/dllserverVersion.rc @@ -0,0 +1,35 @@ +// $Id: dllserverVersion.rc,v 1.5 2002/04/27 18:15:24 cthuang Exp $ +#include +#include "version.h" +#include "buildNumber.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION PACKAGE_MAJOR_VERSION,PACKAGE_MINOR_VERSION,0,BUILD_NUMBER + PRODUCTVERSION PACKAGE_MAJOR_VERSION,PACKAGE_MINOR_VERSION,0,BUILD_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0 +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "tcom in-process server" + VALUE "FileVersion", PACKAGE_VERSION + VALUE "LegalCopyright", "Copyright 2002 by Chin Huang" + VALUE "OriginalFilename", "tcominproc.dll" + VALUE "ProductName", "tcom in-process server" + VALUE "ProductVersion", PACKAGE_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/exemain.cpp b/src/exemain.cpp new file mode 100644 index 0000000..abd3919 --- /dev/null +++ b/src/exemain.cpp @@ -0,0 +1,119 @@ +// $Id: exemain.cpp,v 1.12 2002/07/14 18:42:57 cthuang Exp $ +#pragma warning(disable: 4786) +#include "TclModule.h" +#include "tclRunTime.h" + +// This class implements a COM module for an EXE server. + +class ExeModule: public TclModule +{ + DWORD m_threadId; + HANDLE m_shutdownEvent; + +protected: + virtual DWORD regclsFlags() const; + +public: + // Increment lock count. + virtual void lock(); + + // Decrement lock count. + virtual long unlock(); + + // Wait for the shutdown event to be raised. + void waitForShutdown(); + + // Start thread waiting for shutdown event. + bool startMonitor(DWORD threadId); +}; + +DWORD +ExeModule::regclsFlags () const +{ + return ComModule::regclsFlags() | REGCLS_SUSPENDED; +} + +void +ExeModule::lock() +{ + CoAddRefServerProcess(); +} + +long +ExeModule::unlock() +{ + long count = CoReleaseServerProcess(); + if (count == 0) { + // Notify monitor to exit application. + SetEvent(m_shutdownEvent); + } + return count; +} + +void +ExeModule::waitForShutdown() +{ + WaitForSingleObject(m_shutdownEvent, INFINITE); + CloseHandle(m_shutdownEvent); + PostThreadMessage(m_threadId, WM_QUIT, 0, 0); +} + +// Passed to CreateThread to monitor the shutdown event. + +static DWORD WINAPI +monitorProc (void *pv) +{ + ExeModule *pModule = reinterpret_cast(pv); + pModule->waitForShutdown(); + return 0; +} + +bool +ExeModule::startMonitor (DWORD threadId) +{ + m_threadId = threadId; + + m_shutdownEvent = CreateEvent(NULL, false, false, NULL); + if (m_shutdownEvent == NULL) { + return false; + } + + DWORD myThreadId; + HANDLE h = CreateThread(NULL, 0, monitorProc, this, 0, &myThreadId); + return h != NULL; +} + +extern "C" int WINAPI +WinMain (HINSTANCE /*hInstance*/, + HINSTANCE /*hPrevInstance*/, + LPTSTR lpCmdLine, + int /*nShowCmd*/) +{ + ExeModule module; + module.startMonitor(GetCurrentThreadId()); + + // Get CLSID string from command line. + std::string cmdLine(lpCmdLine); + std::string::size_type clsidEnd = cmdLine.find_first_of(" \t"); + std::string clsidStr(cmdLine, 0, clsidEnd); + + // Evaluate script to register class. + int completionCode = module.registerFactoryByScript(clsidStr); + if (completionCode != TCL_OK) { + return completionCode; + } + + CoResumeClassObjects(); + + MSG msg; + while (GetMessage(&msg, 0, 0, 0)) { + DispatchMessage(&msg); + } + + module.terminate(); + + // Wait for any threads to finish. + Sleep(1000); + + return 0; +} diff --git a/src/exeserver.dsp b/src/exeserver.dsp new file mode 100644 index 0000000..e1b59a5 --- /dev/null +++ b/src/exeserver.dsp @@ -0,0 +1,135 @@ +# Microsoft Developer Studio Project File - Name="exeserver" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=exeserver - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "exeserver.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "exeserver.mak" CFG="exeserver - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "exeserver - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "exeserver - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "exeserver - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "exeserver___Win32_Release" +# PROP BASE Intermediate_Dir "exeserver___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "exeserver_Release" +# PROP Intermediate_Dir "exeserver_Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /Od /I "\tcl\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "TCL_THREADS" /D "USE_TCL_STUBS" /D "USE_NON_CONST" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 Release\tcom.lib rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"exeserver_Release/tcomlocal.exe" /libpath:"\tcl\lib" + +!ELSEIF "$(CFG)" == "exeserver - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "exeserver___Win32_Debug" +# PROP BASE Intermediate_Dir "exeserver___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "exeserver_Debug" +# PROP Intermediate_Dir "exeserver_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "\tcl\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "TCL_THREADS" /D "USE_TCL_STUBS" /D "USE_NON_CONST" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 Debug\tcom.lib rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"exeserver_Debug/tcomlocal.exe" /libpath:"\tcl\lib" + +!ENDIF + +# Begin Target + +# Name "exeserver - Win32 Release" +# Name "exeserver - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\exemain.cpp +# End Source File +# Begin Source File + +SOURCE=.\exeserverVersion.rc +# End Source File +# Begin Source File + +SOURCE=.\RegistryKey.cpp +# End Source File +# Begin Source File + +SOURCE=.\TclInterp.cpp +# End Source File +# Begin Source File + +SOURCE=.\TclModule.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\RegistryKey.h +# End Source File +# Begin Source File + +SOURCE=.\TclInterp.h +# End Source File +# Begin Source File + +SOURCE=.\TclModule.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/src/exeserverVersion.rc b/src/exeserverVersion.rc new file mode 100644 index 0000000..711106b --- /dev/null +++ b/src/exeserverVersion.rc @@ -0,0 +1,35 @@ +// $Id: exeserverVersion.rc,v 1.5 2002/04/27 18:15:24 cthuang Exp $ +#include +#include "version.h" +#include "buildNumber.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION PACKAGE_MAJOR_VERSION,PACKAGE_MINOR_VERSION,0,BUILD_NUMBER + PRODUCTVERSION PACKAGE_MAJOR_VERSION,PACKAGE_MINOR_VERSION,0,BUILD_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0 +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "tcom local server" + VALUE "FileVersion", PACKAGE_VERSION + VALUE "LegalCopyright", "Copyright 2002 by Chin Huang" + VALUE "OriginalFilename", "tcomlocal.exe" + VALUE "ProductName", "tcom local server" + VALUE "ProductVersion", PACKAGE_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/foreachCmd.cpp b/src/foreachCmd.cpp new file mode 100644 index 0000000..d61e3bb --- /dev/null +++ b/src/foreachCmd.cpp @@ -0,0 +1,185 @@ +// $Id: foreachCmd.cpp,v 1.10 2002/05/31 04:03:06 cthuang Exp $ +#include "Extension.h" +#include +#include "Reference.h" +#include "Arguments.h" + +// This Tcl command iterates through the elements in a COM collection. + +int +Extension::foreachCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const objv[]) +{ + if (objc != 4) { + Tcl_WrongNumArgs( + interp, 1, objv, "varList collectionHandle script"); + return TCL_ERROR; + } + + Tcl_Obj *pVarList = objv[1]; + Tcl_Obj *pBody = objv[3]; + + Reference *pCollection = referenceHandles.find(interp, objv[2]); + if (pCollection == 0) { + const char *arg = Tcl_GetStringFromObj(objv[2], 0); + Tcl_AppendResult( + interp, "invalid interface pointer handle ", arg, NULL); + return TCL_ERROR; + } + + // Collections should implement a _NewEnum method which returns an object + // that enumerates the elements. + HRESULT hr; + PositionalArguments arguments; + _variant_t varResult; + + hr = pCollection->invoke( + DISPID_NEWENUM, + DISPATCH_METHOD | DISPATCH_PROPERTYGET, + arguments, + &varResult); + if (FAILED(hr) || V_VT(&varResult) != VT_UNKNOWN) { + Tcl_AppendResult(interp, "object is not a collection", NULL); + return TCL_ERROR; + } + IUnknownPtr pUnk(V_UNKNOWN(&varResult)); + + // Get a specific kind of enumeration. + IEnumVARIANTPtr pEnumVARIANT; + IEnumUnknownPtr pEnumUnknown; + enum EnumKind { ENUM_VARIANT, ENUM_UNKNOWN }; + EnumKind enumKind; + + hr = pUnk->QueryInterface( + IID_IEnumVARIANT, reinterpret_cast(&pEnumVARIANT)); + if (SUCCEEDED(hr)) { + enumKind = ENUM_VARIANT; + } else { + hr = pUnk->QueryInterface( + IID_IEnumUnknown, reinterpret_cast(&pEnumUnknown)); + if (SUCCEEDED(hr)) { + enumKind = ENUM_UNKNOWN; + } + } + + if (FAILED(hr)) { + Tcl_AppendResult(interp, + "Unknown enumerator type: not IEnumVARIANT or IEnumUnknown", NULL); + return TCL_ERROR; + } + + int completionCode; + + int varc; // number of loop variables + completionCode = Tcl_ListObjLength(interp, pVarList, &varc); + if (completionCode != TCL_OK) { + return TCL_ERROR; + } + if (varc < 1) { + Tcl_AppendResult(interp, "foreach varlist is empty", NULL); + return TCL_ERROR; + } + + while (true) { + // If the variable list has been converted to another kind of Tcl + // object, convert it back to a list and refetch the pointer to its + // element array. + Tcl_Obj **varv; + completionCode = + Tcl_ListObjGetElements(interp, pVarList, &varc, &varv); + if (completionCode != TCL_OK) { + return TCL_ERROR; + } + + // Assign values to all loop variables. + int v = 0; + for (; v < varc; ++v) { + TclObject value; + ULONG count; + + switch (enumKind) { + case ENUM_VARIANT: + { + _variant_t elementVar; + hr = pEnumVARIANT->Next(1, &elementVar, &count); + if (hr == S_OK && count > 0) { + value = TclObject(&elementVar, Type::variant(), interp); + } + } + break; + + case ENUM_UNKNOWN: + { + IUnknown *pElement; + hr = pEnumUnknown->Next(1, &pElement, &count); + if (hr == S_OK && count > 0) { + value = referenceHandles.newObj( + interp, Reference::newReference(pElement)); + } + } + break; + } + + if (FAILED(hr)) { + _com_issue_error(hr); + } + if (hr != S_OK || count == 0) { + break; + } + + Tcl_Obj *varValuePtr = Tcl_ObjSetVar2( + interp, varv[v], NULL, value, TCL_LEAVE_ERR_MSG); + if (varValuePtr == NULL) { + return TCL_ERROR; + } + } + + if (v == 0) { + completionCode = TCL_OK; + break; + } + + if (v < varc) { + TclObject empty; + + for (; v < varc; ++v) { + Tcl_Obj *varValuePtr = Tcl_ObjSetVar2( + interp, varv[v], NULL, empty, TCL_LEAVE_ERR_MSG); + if (varValuePtr == NULL) { + return TCL_ERROR; + } + } + } + + // Execute the script body. + completionCode = +#if TCL_MINOR_VERSION >= 1 + Tcl_EvalObjEx(interp, pBody, 0); +#else + Tcl_EvalObj(interp, pBody); +#endif + + if (completionCode == TCL_CONTINUE) { + // do nothing + } else if (completionCode == TCL_BREAK) { + completionCode = TCL_OK; + break; + } else if (completionCode == TCL_ERROR) { + std::ostringstream oss; + oss << "\n (\"foreach\" body line %d)" << interp->errorLine; + Tcl_AddObjErrorInfo( + interp, const_cast(oss.str().c_str()), -1); + break; + } else if (completionCode != TCL_OK) { + break; + } + } + + if (completionCode == TCL_OK) { + Tcl_ResetResult(interp); + } + return completionCode; +} diff --git a/src/importCmd.cpp b/src/importCmd.cpp new file mode 100644 index 0000000..2682c14 --- /dev/null +++ b/src/importCmd.cpp @@ -0,0 +1,534 @@ +// $Id: importCmd.cpp,v 1.26 2002/05/31 04:03:06 cthuang Exp $ +#pragma warning(disable: 4786) +#include "Extension.h" +#include +#include "Reference.h" +#include "TypeLib.h" +#include "TclObject.h" + +// interface currently being parsed +static Interface *s_pCurrentInterface; + +// Parse method parameters from list. + +static int +listObjToParameters (Tcl_Interp *interp, Tcl_Obj *pParameters, Method &method) +{ + int paramCount; + if (Tcl_ListObjLength(interp, pParameters, ¶mCount) != TCL_OK) { + return TCL_ERROR; + } + + for (int i = 0; i < paramCount; ++i) { + Tcl_Obj *pParameter; + if (Tcl_ListObjIndex(interp, pParameters, i, &pParameter) + != TCL_OK) { + return TCL_ERROR; + } + + int paramObjc; + Tcl_Obj **paramObjv; + if (Tcl_ListObjGetElements(interp, pParameter, ¶mObjc, ¶mObjv) + != TCL_OK) { + return TCL_ERROR; + } + Parameter parameter( + Tcl_GetStringFromObj(paramObjv[0], 0), + Tcl_GetStringFromObj(paramObjv[1], 0), + Tcl_GetStringFromObj(paramObjv[2], 0)); + method.addParameter(parameter); + } + + return TCL_OK; +} + +// This Tcl command defines a method. + +int +Extension::methodCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 5) { + Tcl_WrongNumArgs(interp, 1, objv, "dispid returnType name parameters"); + return TCL_ERROR; + } + int dispid; + if (Tcl_GetIntFromObj(interp, objv[1], &dispid) != TCL_OK) { + return TCL_ERROR; + } + char *returnType = Tcl_GetStringFromObj(objv[2], 0); + char *name = Tcl_GetStringFromObj(objv[3], 0); + + Method method(dispid, returnType, name); + if (listObjToParameters(interp, objv[4], method) != TCL_OK) { + return TCL_ERROR; + } + s_pCurrentInterface->addMethod(method); + + return TCL_OK; +} + +// This Tcl command defines a property. + +int +Extension::propertyCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 5 || objc > 6) { + Tcl_WrongNumArgs( + interp, + 1, + objv, + "dispid modes type name ?parameters?"); + return TCL_ERROR; + } + int dispid; + if (Tcl_GetIntFromObj(interp, objv[1], &dispid) != TCL_OK) { + return TCL_ERROR; + } + char *modes = Tcl_GetStringFromObj(objv[2], 0); + char *type = Tcl_GetStringFromObj(objv[3], 0); + char *name = Tcl_GetStringFromObj(objv[4], 0); + + Property property(dispid, modes, type, name); + if (objc == 6) { + if (listObjToParameters(interp, objv[5], property) != TCL_OK) { + return TCL_ERROR; + } + } + s_pCurrentInterface->addProperty(property); + + return TCL_OK; +} + +// This Tcl command queries an interface pointer for a specific interface +// and returns a new interface pointer handle. + +static int +interfaceObjCmd ( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "object"); + return TCL_ERROR; + } + + Reference *pSrcRef = Extension::referenceHandles.find(interp, objv[1]); + if (pSrcRef == 0) { + return TCL_ERROR; + } + + const Interface *pInterface = + reinterpret_cast(clientData); + try { + Tcl_SetObjResult( + interp, + Extension::referenceHandles.newObj( + interp, + Reference::newReference(pSrcRef->unknown(), pInterface))); + } + catch (_com_error &e) { + return Extension::setComErrorResult(interp, e, __FILE__, __LINE__); + } + return TCL_OK; +} + +// This Tcl command defines an interface. + +int +Extension::interfaceCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 3 || objc > 4) { + Tcl_WrongNumArgs(interp, 1, objv, "name iid ?body?"); + return TCL_ERROR; + } + char *name = Tcl_GetStringFromObj(objv[1], 0); + char *iidStr = Tcl_GetStringFromObj(objv[2], 0); + + IID iid; + if (UuidFromString(reinterpret_cast(iidStr), &iid) + != RPC_S_OK) { + Tcl_AppendResult(interp, "cannot convert to IID: ", iidStr, NULL); + return TCL_ERROR; + } + + Interface *pInterface; + if (objc == 4) { + pInterface = + InterfaceManager::instance().newInterface(iid, name); + + s_pCurrentInterface = pInterface; + + int completionCode = +#if TCL_MINOR_VERSION >= 1 + Tcl_EvalObjEx(interp, objv[3], TCL_EVAL_GLOBAL); +#else + Tcl_GlobalEvalObj(interp, objv[3]); +#endif + + if (completionCode != TCL_OK) { + return TCL_ERROR; + } + } else { + pInterface = const_cast( + InterfaceManager::instance().find(iid)); + if (pInterface == 0) { + Tcl_AppendResult(interp, "unknown IID ", iidStr, NULL); + return TCL_ERROR; + } + } + + Tcl_CreateObjCommand( + interp, + name, + interfaceObjCmd, + reinterpret_cast(pInterface), + (Tcl_CmdDeleteProc *)0); + return TCL_OK; +} + +// This Tcl command creates an instance of a COM class and returns an +// interface pointer handle. + +static int +classObjCmd ( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc > 4) { + Tcl_WrongNumArgs( + interp, + 1, + objv, + "?-inproc? ?-local? ?-remote? ?-withevents servant? ?hostName?"); + return TCL_ERROR; + } + + DWORD clsCtx = CLSCTX_SERVER; + bool withEvents = false; + TclObject servant; + + int i = 1; + for (; i < objc; ++i) { + static char *options[] = { + "-inproc", "-local", "-remote", "-withevents", NULL + }; + enum OptionEnum { + OPTION_INPROC, OPTION_LOCAL, OPTION_REMOTE, OPTION_WITHEVENTS + }; + + int index; + if (Tcl_GetIndexFromObj(NULL, objv[i], options, "option", 0, &index) + != TCL_OK) { + break; + } + + switch (index) { + case OPTION_INPROC: + clsCtx = CLSCTX_INPROC_SERVER; + break; + case OPTION_LOCAL: + clsCtx = CLSCTX_LOCAL_SERVER; + break; + case OPTION_REMOTE: + clsCtx = CLSCTX_REMOTE_SERVER; + break; + case OPTION_WITHEVENTS: + if (i + 1 < objc) { + withEvents = true; + servant = objv[++i]; + } + break; + } + } + + char *hostName = (i < objc) ? Tcl_GetStringFromObj(objv[i], 0) : 0; + if (clsCtx == CLSCTX_REMOTE_SERVER && hostName == 0) { + Tcl_AppendResult( + interp, "hostname required with -remote option", NULL); + return TCL_ERROR; + } + + Class *pClass = reinterpret_cast(clientData); + try { + Reference *pRef = Reference::createInstance( + pClass->clsid(), + pClass->defaultInterface(), + clsCtx, + hostName); + + if (withEvents) { + if (pClass->sourceInterface() == 0) { + Tcl_AppendResult( + interp, "no default event source", NULL); + return TCL_ERROR; + } + pRef->advise(interp, *(pClass->sourceInterface()), servant); + } + + Tcl_SetObjResult( + interp, + Extension::referenceHandles.newObj(interp, pRef)); + } + catch (_com_error &e) { + return Extension::setComErrorResult(interp, e, __FILE__, __LINE__); + } + return TCL_OK; +} + +static void +classCmdDeleteProc (ClientData clientData) +{ + delete reinterpret_cast(clientData); +} + +// This Tcl command defines a COM class. + +int +Extension::classCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 4 || objc > 5) { + Tcl_WrongNumArgs(interp, 1, objv, "name clsid defaultIID ?sourceIID?"); + return TCL_ERROR; + } + char *name = Tcl_GetStringFromObj(objv[1], 0); + char *clsidStr = Tcl_GetStringFromObj(objv[2], 0); + char *defaultStr = Tcl_GetStringFromObj(objv[3], 0); + char *sourceStr = (objc == 5) ? Tcl_GetStringFromObj(objv[4], 0) : 0; + + // Convert CLSID. + CLSID clsid; + if (UuidFromString(reinterpret_cast(clsidStr), &clsid) + != RPC_S_OK) { + Tcl_AppendResult(interp, "cannot convert to CLSID: ", clsidStr, NULL); + return TCL_ERROR; + } + + // Convert default IID. + IID iid; + + if (UuidFromString(reinterpret_cast(defaultStr), &iid) + != RPC_S_OK) { + Tcl_AppendResult(interp, "cannot convert to IID: ", defaultStr, NULL); + return TCL_ERROR; + } + + const Interface *pDefaultInterface = InterfaceManager::instance().find(iid); + if (pDefaultInterface == 0) { + Tcl_AppendResult(interp, "unknown interface ", defaultStr, NULL); + return TCL_ERROR; + } + + // Convert source IID. + const Interface *pSourceInterface; + if (sourceStr != 0) { + if (UuidFromString(reinterpret_cast(sourceStr), &iid) + != RPC_S_OK) { + Tcl_AppendResult(interp, "cannot convert to IID: ", sourceStr, NULL); + return TCL_ERROR; + } + + pSourceInterface = InterfaceManager::instance().find(iid); + if (pSourceInterface == 0) { + Tcl_AppendResult(interp, "unknown interface ", sourceStr, NULL); + return TCL_ERROR; + } + } else { + pSourceInterface = 0; + } + + Tcl_CreateObjCommand( + interp, + name, + classObjCmd, + new Class(name, clsid, pDefaultInterface, pSourceInterface), + classCmdDeleteProc); + return TCL_OK; +} + +const Class * +Extension::findClassByCmdName (Tcl_Interp *interp, Tcl_Obj *pName) +{ + char *nameStr = Tcl_GetStringFromObj(pName, 0); + + Tcl_CmdInfo cmdInfo; + if (Tcl_GetCommandInfo(interp, nameStr, &cmdInfo) == 0) { + return 0; + } + + if (cmdInfo.objProc == classObjCmd) { + return static_cast(cmdInfo.objClientData); + } + return 0; +} + +const Interface * +Extension::findInterfaceByCmdName (Tcl_Interp *interp, Tcl_Obj *pNameObj) +{ + char *pName = Tcl_GetStringFromObj(pNameObj, 0); + + // Check if it's the name of an interface. + Tcl_CmdInfo cmdInfo; + if (Tcl_GetCommandInfo(interp, pName, &cmdInfo) == 0) { + return 0; + } + + if (cmdInfo.objProc == interfaceObjCmd) { + return reinterpret_cast(cmdInfo.objClientData); + } + + return 0; +} + +// This Tcl command reads interface and class information from a type library +// and creates Tcl commands for accessing that information. + +int +Extension::importCmd ( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "typeLibrary ?namespace?"); + return TCL_ERROR; + } + + Extension *pExtension = + static_cast(clientData); + pExtension->initializeCom(); + + char *typeLibName = Tcl_GetStringFromObj(objv[1], 0); + + try { + TypeLib *pTypeLib = TypeLib::load(typeLibName); + + // Create the new Tcl commands in a namespace named after the type + // library, unless a specific namespace was given. + std::string namesp; + if (objc > 2) { + namesp = Tcl_GetStringFromObj(objv[2], 0); + } else { + namesp = pTypeLib->name(); + } + std::string fullyQualifiedNamespace = "::" + namesp; + + std::ostringstream script; + script << "namespace eval " << fullyQualifiedNamespace << " {" + << std::endl; + + // Export interface commands. + const TypeLib::Interfaces &interfaces = pTypeLib->interfaces(); + TypeLib::Interfaces::const_iterator pInterface; + for (pInterface = interfaces.begin(); pInterface != interfaces.end(); + ++pInterface) { + script << "namespace export " << (*pInterface)->name() << std::endl; + } + + // Export class commands. + const TypeLib::Classes &classes = pTypeLib->classes(); + TypeLib::Classes::const_iterator pClass; + for (pClass = classes.begin(); pClass != classes.end(); ++pClass) { + script << "namespace export " << pClass->name() << std::endl; + } + + // Generate IID and CLSID constants. + script << "array set __uuidof {" << std::endl; + + for (pInterface = interfaces.begin(); pInterface != interfaces.end(); + ++pInterface) { + script << (*pInterface)->name() << ' ' << (*pInterface)->iidString() + << std::endl; + } + + for (pClass = classes.begin(); pClass != classes.end(); ++pClass) { + script << pClass->name() << ' ' << pClass->clsidString() + << std::endl; + } + + script << '}' << std::endl; // end of array set + + // Generate enumerations. + const TypeLib::Enums &enums = pTypeLib->enums(); + for (TypeLib::Enums::const_iterator pEnum = enums.begin(); + pEnum != enums.end(); ++pEnum) { + script << "array set " << pEnum->name() << " {" << std::endl; + + for (Enum::const_iterator p = pEnum->begin(); p != pEnum->end(); + ++p) { + script << p->first << ' ' << p->second << std::endl; + } + + script << '}' << std::endl; // end of array set + } + + script << '}' << std::endl; // end of namespace + +#if TCL_MINOR_VERSION >= 1 + Tcl_EvalEx( + interp, + const_cast(script.str().c_str()), + -1, + TCL_EVAL_GLOBAL); +#else + Tcl_Eval(interp, const_cast(script.str().c_str())); +#endif + + // Create interface commands. + for (pInterface = interfaces.begin(); pInterface != interfaces.end(); + ++pInterface) { + std::string fullyQualifiedName = + fullyQualifiedNamespace + "::" + (*pInterface)->name(); + + Tcl_CreateObjCommand( + interp, + const_cast(fullyQualifiedName.c_str()), + interfaceObjCmd, + const_cast(*pInterface), + 0); + } + + // Create class commands. + for (pClass = classes.begin(); pClass != classes.end(); ++pClass) { + std::string fullyQualifiedName = + fullyQualifiedNamespace + "::" + pClass->name(); + + Tcl_CreateObjCommand( + interp, + const_cast(fullyQualifiedName.c_str()), + classObjCmd, + new Class(*pClass), + classCmdDeleteProc); + } + + // Return the library name. + Tcl_AppendResult(interp, pTypeLib->name().c_str(), NULL); + + delete pTypeLib; + } + catch (_com_error &e) { + return setComErrorResult(interp, e, __FILE__, __LINE__); + } + + return TCL_OK; +} diff --git a/src/infoCmd.cpp b/src/infoCmd.cpp new file mode 100644 index 0000000..2a3dffd --- /dev/null +++ b/src/infoCmd.cpp @@ -0,0 +1,269 @@ +// $Id: infoCmd.cpp,v 1.31 2002/04/13 03:53:57 cthuang Exp $ +#include "Extension.h" +#include "TclObject.h" +#include "Reference.h" + +static int interfaceObjCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); +HandleSupport Extension::interfaceHolderHandles(interfaceObjCmd); + +// Convert type description to a Tcl list representation. + +static TclObject +typeToListObj (const Type &type) +{ + TclObject list(Tcl_NewListObj(0, 0)); + + list.lappend( + Tcl_NewStringObj(const_cast(type.toString().c_str()), -1)); + + for (unsigned i = 0; i < type.pointerCount(); ++i) { + list.lappend(Tcl_NewStringObj("*", -1)); + } + + return list; +} + +// Convert parameter description to a Tcl list representation. + +static TclObject +parameterToListObj (const Parameter ¶meter) +{ + TclObject list(Tcl_NewListObj(0, 0)); + + // Put parameter passing modes. + TclObject modes(Tcl_NewListObj(0, 0)); + + if (parameter.flags() & PARAMFLAG_FIN) { + modes.lappend(Tcl_NewStringObj("in", -1)); + } + if (parameter.flags() & PARAMFLAG_FOUT) { + modes.lappend(Tcl_NewStringObj("out", -1)); + } + list.lappend(modes); + + // Put parameter type. + list.lappend(typeToListObj(parameter.type())); + + // Put parameter name. + list.lappend( + Tcl_NewStringObj(const_cast(parameter.name().c_str()), -1)); + + return list; +} + +// Convert method description to a Tcl list representation. + +static TclObject +methodToListObj (const Method &method) +{ + TclObject list(Tcl_NewListObj(0, 0)); + + // Put member id. + list.lappend(Tcl_NewIntObj(method.memberid())); + + // Put return type. + list.lappend(typeToListObj(method.type())); + + // Put method name. + list.lappend( + Tcl_NewStringObj(const_cast(method.name().c_str()), -1)); + + // Put parameters. + TclObject parameterList(Tcl_NewListObj(0, 0)); + + const Method::Parameters ¶meters = method.parameters(); + for (Method::Parameters::const_iterator p = parameters.begin(); + p != parameters.end(); ++p) { + parameterList.lappend(parameterToListObj(*p)); + } + + list.lappend(parameterList); + + return list; +} + +// Implement interface descriptor object command. + +static int +interfaceObjCmd ( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "method ?arg ...?"); + return TCL_ERROR; + } + + static char *options[] = { + "iid", "methods", "name", "properties", NULL + }; + enum MethodEnum { + IID, METHODS, NAME, PROPERTIES + }; + + int index; + if (Tcl_GetIndexFromObj(interp, objv[1], options, "method", 0, &index) + != TCL_OK) { + return TCL_ERROR; + } + + const InterfaceHolder *pHolder = + reinterpret_cast(clientData); + const Interface *pInterface = pHolder->interfaceDesc(); + + switch (index) { + case IID: + // Get IID. + Tcl_AppendResult(interp, pInterface->iidString().c_str(), NULL); + return TCL_OK; + + case METHODS: + // Get method descriptions. + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + return TCL_ERROR; + } else { + TclObject methodList(Tcl_NewListObj(0, 0)); + + const Interface::Methods &methods = pInterface->methods(); + for (Interface::Methods::const_iterator p = methods.begin(); + p != methods.end(); ++p) { + methodList.lappend(methodToListObj(*p)); + } + + Tcl_SetObjResult(interp, methodList); + } + return TCL_OK; + + case NAME: + // Get interface name. + Tcl_AppendResult(interp, pInterface->name().c_str(), NULL); + return TCL_OK; + + case PROPERTIES: + // Get property descriptions. + // Returns a list where each element is a list consisting of + // { dispatchID {modes} {type} name {parameters} } + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + return TCL_ERROR; + } else { + TclObject propertyList(Tcl_NewListObj(0, 0)); + + const Interface::Properties &properties = pInterface->properties(); + for (Interface::Properties::const_iterator p = properties.begin(); + p != properties.end(); ++p) { + TclObject property(Tcl_NewListObj(0, 0)); + + // Set dispatch ID. + property.lappend(Tcl_NewIntObj(p->memberid())); + + // Set read/write modes. + TclObject modes(Tcl_NewListObj(0, 0)); + + if (!p->readOnly()) { + modes.lappend(Tcl_NewStringObj("in", -1)); + } + modes.lappend(Tcl_NewStringObj("out", -1)); + + property.lappend(modes); + + // Set property type. + property.lappend(typeToListObj(p->type())); + + // Put property name. + property.lappend(Tcl_NewStringObj( + const_cast(p->name().c_str()), -1)); + + // Put parameters. + const Property::Parameters ¶meters = p->parameters(); + if (parameters.size() > 0) { + TclObject parameterList(Tcl_NewListObj(0, 0)); + + for (Property::Parameters::const_iterator q = + parameters.begin(); q != parameters.end(); ++q) { + parameterList.lappend(parameterToListObj(*q)); + } + + property.lappend(parameterList); + } + + propertyList.lappend(property); + } + + Tcl_SetObjResult(interp, propertyList); + } + return TCL_OK; + } + + return TCL_ERROR; +} + +// This Tcl command returns descriptions of object interfaces. + +int +Extension::infoCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?arg ...?"); + return TCL_ERROR; + } + + static char *options[] = { + "interface", NULL + }; + enum SubCommandEnum { + INTERFACE + }; + + int index; + if (Tcl_GetIndexFromObj(interp, objv[1], options, "subcommand", 0, + &index) != TCL_OK) { + return TCL_ERROR; + } + + switch (index) { + case INTERFACE: + // Create interface description object. + { + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "handle"); + return TCL_ERROR; + } + const Interface *pInterface; + Tcl_Obj *pObj = objv[2]; + + Reference *pRef = Extension::referenceHandles.find(interp, pObj); + if (pRef != 0) { + pInterface = pRef->interfaceDesc(); + } else { + pInterface = Extension::findInterfaceByCmdName(interp, pObj); + if (pInterface == 0) { + const Class *pClass = + Extension::findClassByCmdName(interp, pObj); + if (pClass != 0) { + pInterface = pClass->defaultInterface(); + } + } + } + + if (pInterface == 0) { + Tcl_AppendResult(interp, "cannot get type information", NULL); + return TCL_ERROR; + } + + InterfaceHolder *pHolder = new InterfaceHolder(pInterface); + Tcl_Obj *pHandle = + interfaceHolderHandles.newObj(interp, pHolder); + Tcl_SetObjResult(interp, pHandle); + } + return TCL_OK; + } + return TCL_ERROR; +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..160c515 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,66 @@ +// $Id: main.cpp,v 1.70 2002/07/14 18:42:57 cthuang Exp $ +#pragma warning(disable: 4786) +#include "ComModule.h" +#include "Extension.h" +#include "TclObject.h" +#include "version.h" +#include "tclRunTime.h" + +/* + * This procedure performs application-specific initialization. + * Most applications, especially those that incorporate additional + * packages, will have their own version of this procedure. + * + * Results: + * Returns a standard Tcl completion code, and leaves an error + * message in interp->result if an error occurs. + * + * Side effects: + * Depends on the startup script. + */ +extern "C" DLLEXPORT int +Tcom_Init (Tcl_Interp *interp) +{ +#ifdef USE_TCL_STUBS + // Stubs were introduced in Tcl 8.1. + if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { + return TCL_ERROR; + } +#endif + + // Get pointers to Tcl's built-in internal representation types. + TclTypes::initialize(); + + Extension *pExtension = new Extension(interp); + pExtension->concurrencyModel(COINIT_APARTMENTTHREADED); + + // Initialize handle support. + CmdNameType::instance(); + new HandleNameToRepMap(interp); + + return Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION); +} + +/* + * This procedure initializes commands for a safe interpreter. + * You would leave out of this procedure any commands you deemed unsafe. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + */ +extern "C" DLLEXPORT int +Tcom_SafeInit ( + Tcl_Interp *interp) +{ +#ifdef USE_TCL_STUBS + // Stubs were introduced in Tcl 8.1. + if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { + return TCL_ERROR; + } +#endif + + return Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION); +} diff --git a/src/mutex.h b/src/mutex.h new file mode 100644 index 0000000..3ee1f72 --- /dev/null +++ b/src/mutex.h @@ -0,0 +1,73 @@ +// $Id: mutex.h,v 1.7 2002/04/13 03:53:57 cthuang Exp $ +#ifndef MUTEX_H +#define MUTEX_H + +#define WIN32_LEAN_AND_MEAN +#include + +// This class is used for mutual-exclusion synchronization. + +class CriticalSectionMutex +{ + CRITICAL_SECTION m_cs; + + // Disallow others from copying instances of this class. + CriticalSectionMutex(const CriticalSectionMutex &); // not implemented + void operator=(const CriticalSectionMutex &); // not implemented + +public: + CriticalSectionMutex () + { InitializeCriticalSection(&m_cs); } + + ~CriticalSectionMutex () + { DeleteCriticalSection(&m_cs); } + + void enter () + { EnterCriticalSection(&m_cs); } + + void leave () + { LeaveCriticalSection(&m_cs); } +}; + +// This class mirrors the operations of a mutex except the operations do +// nothing. + +class FakeMutex +{ +public: + void enter () + { } + + void leave () + { } +}; + +#ifdef TCL_THREADS +typedef CriticalSectionMutex Mutex; +#else +typedef FakeMutex Mutex; +#endif + +// This class locks a mutex when constructed and unlocks it when destroyed. + +class SingleLock +{ + Mutex &m_mutex; + +public: + SingleLock (Mutex &mutex): + m_mutex(mutex) + { m_mutex.enter(); } + + ~SingleLock () + { m_mutex.leave(); } +}; + +#ifdef TCL_THREADS +#define LOCK_MUTEX(mutex) \ + SingleLock criticalSectionLock(const_cast(mutex)); +#else +#define LOCK_MUTEX(mutex) +#endif + +#endif diff --git a/src/naCmd.cpp b/src/naCmd.cpp new file mode 100644 index 0000000..2b6f4d1 --- /dev/null +++ b/src/naCmd.cpp @@ -0,0 +1,93 @@ +// $Id: naCmd.cpp,v 1.6 2002/04/27 18:15:24 cthuang Exp $ +#include "Extension.h" +#include + +// The string representation is the same for all objects of this type. + +static char naStringRep[] = PACKAGE_NAMESPACE "NA"; + +static void +naUpdateString (Tcl_Obj *pObj) +{ + pObj->length = sizeof(naStringRep) - 1; + pObj->bytes = Tcl_Alloc(pObj->length + 1); + strcpy(pObj->bytes, naStringRep); +} + +// Do not allow conversion from other types. + +static int +naSetFromAny (Tcl_Interp *interp, Tcl_Obj *) +{ + if (interp != NULL) { + Tcl_AppendResult( + interp, "cannot convert to ", Extension::naType.name, NULL); + } + return TCL_ERROR; +} + +Tcl_ObjType Extension::naType = { + naStringRep, + NULL, + NULL, + naUpdateString, + naSetFromAny +}; + +// Create an NA object. + +Tcl_Obj * +Extension::newNaObj () +{ + Tcl_Obj *pObj = Tcl_NewObj(); + Tcl_InvalidateStringRep(pObj); + pObj->typePtr = &naType; + return pObj; +} + +// This Tcl command returns an object used to represent a missing optional +// argument. + +int +Extension::naCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc == 1) { + // Return a missing argument token. + Tcl_SetObjResult(interp, newNaObj()); + return TCL_OK; + } + + static char *options[] = { + "ismissing", NULL + }; + enum SubCommandEnum { + ISMISSING + }; + + int index; + if (Tcl_GetIndexFromObj(interp, objv[1], options, "subcommand", 0, + &index) != TCL_OK) { + return TCL_ERROR; + } + + switch (index) { + case ISMISSING: + // Return true if the object is a missing argument token. + { + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "object"); + return TCL_ERROR; + } + + Tcl_SetObjResult( + interp, + Tcl_NewBooleanObj(objv[2]->typePtr == &naType)); + } + return TCL_OK; + } + return TCL_ERROR; +} diff --git a/src/nullCmd.cpp b/src/nullCmd.cpp new file mode 100644 index 0000000..91fc3aa --- /dev/null +++ b/src/nullCmd.cpp @@ -0,0 +1,58 @@ +// $Id: nullCmd.cpp,v 1.9 2002/04/27 18:15:24 cthuang Exp $ +#include "Extension.h" +#include + +// The string representation is the same for all objects of this type. + +static char nullStringRep[] = PACKAGE_NAMESPACE "NULL"; + +static void +nullUpdateString (Tcl_Obj *pObj) +{ + pObj->length = sizeof(nullStringRep) - 1; + pObj->bytes = Tcl_Alloc(pObj->length + 1); + strcpy(pObj->bytes, nullStringRep); +} + +// Do not allow conversion from other types. + +static int +nullSetFromAny (Tcl_Interp *interp, Tcl_Obj *) +{ + if (interp != NULL) { + Tcl_AppendResult( + interp, "cannot convert to ", Extension::nullType.name, NULL); + } + return TCL_ERROR; +} + +Tcl_ObjType Extension::nullType = { + nullStringRep, + NULL, + NULL, + nullUpdateString, + nullSetFromAny +}; + +// This Tcl command returns a null object which be used to pass a null pointer +// argument. + +int +Extension::nullCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 1) { + Tcl_WrongNumArgs(interp, 1, objv, NULL); + return TCL_ERROR; + } + + Tcl_Obj *pObj = Tcl_NewObj(); + Tcl_InvalidateStringRep(pObj); + pObj->typePtr = &nullType; + + Tcl_SetObjResult(interp, pObj); + return TCL_OK; +} diff --git a/src/objectCmd.cpp b/src/objectCmd.cpp new file mode 100644 index 0000000..9f2c010 --- /dev/null +++ b/src/objectCmd.cpp @@ -0,0 +1,288 @@ +// $Id: objectCmd.cpp,v 1.30 2002/04/27 18:15:24 cthuang Exp $ +#pragma warning(disable: 4786) +#include "Extension.h" +#include +#include "ComObject.h" +#include "ComObjectFactory.h" +#include "ComModule.h" + +// Set the string representation to match the internal representation. + +static void +unknownPointerUpdateString (Tcl_Obj *pObj) +{ + std::ostringstream oss; + oss << "0x" << std::hex << pObj->internalRep.otherValuePtr; + std::string stringRep(oss.str()); + + pObj->length = stringRep.size(); + pObj->bytes = Tcl_Alloc(pObj->length + 1); + stringRep.copy(pObj->bytes, pObj->length); + pObj->bytes[pObj->length] = '\0'; +} + +// Set internal representation from string representation. + +static int +unknownPointerSetFromAny (Tcl_Interp *interp, Tcl_Obj *) +{ + // Do not allow conversion from other types. + if (interp != NULL) { + Tcl_AppendResult( + interp, + "cannot convert to ", + Extension::unknownPointerType.name, + NULL); + } + return TCL_ERROR; +} + +Tcl_ObjType Extension::unknownPointerType = { + PACKAGE_NAMESPACE "UnknownPointer", + NULL, + NULL, + unknownPointerUpdateString, + unknownPointerSetFromAny +}; + +// This Tcl command registers a factory that creates COM objects. + +static int +objectRegisterFactoryCmd ( + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *CONST objv[]) /* The argument objects. */ +{ + bool registerActiveOpt = false; + bool singletonOpt = false; + + int i = 2; + for (; i < objc; ++i) { + static char *options[] = { + "-registeractive", "-singleton", NULL + }; + enum OptionEnum { + OPTION_REGISTERACTIVE, + OPTION_SINGLETON + }; + + int index; + if (Tcl_GetIndexFromObj(NULL, objv[i], options, "option", 0, &index) + != TCL_OK) { + break; + } + + switch (index) { + case OPTION_REGISTERACTIVE: + registerActiveOpt = true; + break; + case OPTION_SINGLETON: + singletonOpt = true; + break; + } + } + + if (objc - i < 2 || objc - i > 3) { + Tcl_WrongNumArgs( + interp, + 2, + objv, + "?-singleton? class constructCommand ?destroyCommand?"); + return TCL_ERROR; + } + + const Class *pClass = Extension::findClassByCmdName(interp, objv[i]); + if (pClass == 0) { + char *className = Tcl_GetStringFromObj(objv[i], 0); + Tcl_AppendResult(interp, "unknown class ", className, NULL); + return TCL_ERROR; + } + + TclObject constructor(objv[i + 1]); + + TclObject destructor; + if (objc - i == 3) { + destructor = objv[i + 2]; + } + + ComObjectFactory *pFactory; + if (singletonOpt) { + pFactory = new SingletonObjectFactory( + pClass->interfaces(), + interp, + constructor, + destructor, + registerActiveOpt); + } else { + pFactory = new ComObjectFactory( + pClass->interfaces(), + interp, + constructor, + destructor, + registerActiveOpt); + } + ComModule::instance().registerFactory(pClass->clsid(), pFactory); + return TCL_OK; +} + +// Find interface description from imported interface name. +// On error, put a message in the Tcl interpreter result and return 0. + +static const Interface * +findInterface (Tcl_Interp *interp, Tcl_Obj *pName) +{ + const Interface *pInterface = Extension::findInterfaceByCmdName(interp, pName); + if (pInterface == 0) { + char *nameStr = Tcl_GetStringFromObj(pName, 0); + Tcl_AppendResult( + interp, "unknown interface name: ", nameStr, NULL); + } + return pInterface; +} + +// This Tcl command creates a COM object. + +static int +objectCreateCmd ( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + bool registerActiveOpt = false; + + int i = 2; + for (; i < objc; ++i) { + static char *options[] = { + "-registeractive", NULL + }; + enum OptionEnum { + OPTION_REGISTERACTIVE + }; + + int index; + if (Tcl_GetIndexFromObj(NULL, objv[i], options, "option", 0, &index) + != TCL_OK) { + break; + } + + switch (index) { + case OPTION_REGISTERACTIVE: + registerActiveOpt = true; + break; + } + } + + if (objc - i < 2 || objc - i > 3) { + Tcl_WrongNumArgs( + interp, + 2, + objv, + "?-registeractive? class servant ?destroyCommand?"); + return TCL_ERROR; + } + + TclObject servant(objv[i + 1]); + + TclObject destructor; + if (objc - i == 3) { + destructor = objv[i + 2]; + } + + try { + ComObject *pComObject; + + const Class *pClass = Extension::findClassByCmdName(interp, objv[i]); + if (pClass != 0) { + pComObject = ComObject::newInstance( + pClass->interfaces(), + interp, + servant, + destructor); + + if (registerActiveOpt) { + pComObject->registerActiveObject(pClass->clsid()); + } + } else { + // Check if the argument is a list of imported interface names. + int interfaceCount; + Tcl_Obj **interfaceObj; + int result = Tcl_ListObjGetElements( + interp, objv[i], &interfaceCount, &interfaceObj); + if (result != TCL_OK) { + return TCL_ERROR; + } + if (interfaceCount < 1) { + Tcl_AppendResult( + interp, "must specify at least one interface name", NULL); + return TCL_ERROR; + } + + Class::Interfaces interfaces; + for (int i = 0; i < interfaceCount; ++i) { + const Interface *pInterface = + findInterface(interp, interfaceObj[i]); + if (pInterface == 0) { + return TCL_ERROR; + } + interfaces.push_back(pInterface); + } + + pComObject = ComObject::newInstance( + interfaces, + interp, + servant, + destructor); + } + + Tcl_Obj *pObj = Tcl_NewObj(); + Tcl_InvalidateStringRep(pObj); + pObj->typePtr = &Extension::unknownPointerType; + pObj->internalRep.otherValuePtr = pComObject->unknown(); + + Tcl_SetObjResult(interp, pObj); + } + catch (_com_error &e) { + return Extension::setComErrorResult(interp, e, __FILE__, __LINE__); + } + return TCL_OK; +} + +// This Tcl command provides operations for creating COM objects. + +int +Extension::objectCmd ( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?arg ...?"); + return TCL_ERROR; + } + + Extension *pExtension = + static_cast(clientData); + pExtension->initializeCom(); + + static char *options[] = { + "create", "registerfactory", NULL + }; + enum SubCommandEnum { + CREATE, REGISTER_FACTORY + }; + + int index; + if (Tcl_GetIndexFromObj(interp, objv[1], options, "subcommand", 0, + &index) != TCL_OK) { + return TCL_ERROR; + } + + switch (index) { + case CREATE: + return objectCreateCmd(interp, objc, objv); + case REGISTER_FACTORY: + return objectRegisterFactoryCmd(interp, objc, objv); + } + return TCL_ERROR; +} diff --git a/src/refCmd.cpp b/src/refCmd.cpp new file mode 100644 index 0000000..6b4e641 --- /dev/null +++ b/src/refCmd.cpp @@ -0,0 +1,772 @@ +// $Id: refCmd.cpp,v 1.43 2002/06/12 02:14:08 cthuang Exp $ +#pragma warning(disable: 4786) +#include "Extension.h" +#include +#include "Reference.h" +#include "TypeInfo.h" +#include "TclObject.h" +#include "Arguments.h" + +static int referenceObjCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); +HandleSupport Extension::referenceHandles(referenceObjCmd); + +// Check if the object implements ISupportErrorInfo. If it does, get the +// error information. Return true if successful. + +static bool +getErrorInfo (Reference *pReference, IErrorInfo **ppErrorInfo) +{ + const Interface *pInterface = pReference->interfaceDesc(); + if (pInterface == 0) { + return false; + } + + ISupportErrorInfoPtr pSupportErrorInfo; + HRESULT hr = pReference->unknown()->QueryInterface( + IID_ISupportErrorInfo, reinterpret_cast(&pSupportErrorInfo)); + if (FAILED(hr)) { + return false; + } + + if (pSupportErrorInfo->InterfaceSupportsErrorInfo(pInterface->iid()) + != S_OK) { + return false; + } + + return GetErrorInfo(0, ppErrorInfo) == S_OK; +} + +// Set the Tcl errorCode variable and the Tcl interpreter result. +// Returns TCL_ERROR. + +static int +setErrorCodeAndResult ( + Tcl_Interp *interp, + HRESULT hresult, + const _bstr_t &description, + const char *file, + int line) +{ + TclObject errorCode(Tcl_NewListObj(0, 0)); + errorCode.lappend(Tcl_NewStringObj("COM", -1)); + + TclObject result(Tcl_NewListObj(0, 0)); + + // Append HRESULT value in hexadecimal string format. + std::ostringstream hrOut; + hrOut << "0x" << std::hex << hresult; + TclObject hrObj(hrOut.str()); + errorCode.lappend(hrObj); + result.lappend(hrObj); + + // Append description. + const wchar_t *pWide = static_cast(description); + if (pWide == 0) { + pWide = L"Unknown error"; + } + TclObject descriptionObj(pWide); + errorCode.lappend(descriptionObj); + result.lappend(descriptionObj); + +#ifndef NDEBUG + // Append file and line number. + std::ostringstream fileLine; + fileLine << file << ' ' << line; + TclObject fileLineObj(fileLine.str()); + result.lappend(fileLineObj); +#endif + + Tcl_SetObjErrorCode(interp, errorCode); + Tcl_SetObjResult(interp, result); + return TCL_ERROR; +} + +int +Extension::setComErrorResult ( + Tcl_Interp *interp, _com_error &e, const char *file, int line) +{ + // Get description. + _bstr_t description; + +#if TCL_MINOR_VERSION >= 2 + // Uses Unicode functions introduced in Tcl 8.2. + wchar_t *pMessage = 0; + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + e.Error(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&pMessage), + 0, + NULL); + + if (pMessage != 0) { + int nLen = wcslen(pMessage); + if (nLen > 1 && pMessage[nLen - 1] == '\n') { + --nLen; + if (nLen > 1 && pMessage[nLen - 1] == '\r') { + --nLen; + } + } + pMessage[nLen] = '\0'; + + description = _bstr_t(pMessage); + } else { + // FormatMessageW doesn't seem to work on Windows 95/98. + description = _bstr_t(e.ErrorMessage()); + } + LocalFree(pMessage); +#else + description = _bstr_t(e.ErrorMessage()); +#endif + + return setErrorCodeAndResult(interp, e.Error(), description, file, line); +} + +// Invoke a method or property. + +static int +invoke (Tcl_Interp *interp, + int objc, // number of arguments + Tcl_Obj *CONST objv[], // arguments + Reference *pReference, + const Method *pMethod, + bool namedArgOpt, + TypedArguments &arguments, + WORD dispatchFlags) +{ + // Set up return value. + _variant_t returnValue; + VARIANT *pReturnValue = (pMethod->type().vartype() == VT_VOID) + ? 0 : &returnValue; + + // Invoke it. + HRESULT hr; + if (namedArgOpt) { + hr = pReference->invokeDispatch( + pMethod->memberid(), + dispatchFlags, + arguments, + pReturnValue); + } else { + hr = pReference->invoke( + pMethod->memberid(), + dispatchFlags, + arguments, + pReturnValue); + } + if (FAILED(hr)) { + _com_issue_error(hr); + } + + // Store values returned from out parameters. + arguments.storeOutValues(interp, objc, objv, pMethod->parameters()); + + // Convert return value. + if (pReturnValue != 0) { + TclObject value(pReturnValue, pMethod->type(), interp); + Tcl_SetObjResult(interp, value); + } + return TCL_OK; +} + +// Set Tcl result to a wrong number of arguments error message. + +static void +wrongNumArgs ( + Tcl_Interp *interp, // current interpreter + Tcl_Obj *CONST objv[], // method name + const Method::Parameters ¶meters) // expected parameters +{ + if (parameters.size() > 0) { + std::ostringstream paramNames; + bool first = true; + for (Property::Parameters::const_iterator p = + parameters.begin(); p != parameters.end(); ++p) { + if (first) { + first = false; + } else { + paramNames << ' '; + } + paramNames << p->name(); + } + Tcl_WrongNumArgs( + interp, 1, objv, const_cast(paramNames.str().c_str())); + } else { + Tcl_WrongNumArgs(interp, 1, objv, 0); + } + +} + +// Get or put an object property. + +static int +invokeProperty ( + Tcl_Interp *interp, // Current interpreter + int objc, + Tcl_Obj *CONST objv[], // property name and arguments + Reference *pReference, + const Property *pProperty) +{ + WORD dispatchFlags; + const Property::Parameters ¶meters = pProperty->parameters(); + + if (objc > parameters.size() + 2) { + wrongNumArgs(interp, objv, parameters); + return TCL_ERROR; + + } else if (objc == parameters.size() + 2) { + // Put property. + dispatchFlags = pProperty->putDispatchFlag(); + + } else { + // Get property. + dispatchFlags = DISPATCH_PROPERTYGET; + } + + PositionalArguments arguments; + int result = arguments.initialize( + interp, objc - 1, objv + 1, *pProperty, dispatchFlags); + if (result != TCL_OK) { + return result; + } + + return invoke( + interp, + objc - 1, + objv + 1, + pReference, + pProperty, + false, + arguments, + dispatchFlags); +} + +// Invoke a method without any type information using IDispatch. +// Return a Tcl completion code. + +static int +invokeWithoutInterfaceDesc ( + Tcl_Interp *interp, + Reference *pReference, + int objc, + Tcl_Obj *CONST objv[], // method name and arguments + WORD dispatchFlags) +{ + HRESULT hr; + + IDispatch *pDispatch = pReference->dispatch(); + if (pDispatch == 0) { + Tcl_AppendResult(interp, "object does not implement IDispatch", NULL); + return TCL_ERROR; + } + + // Ask for named method or property. + const char *name = Tcl_GetStringFromObj(objv[0], 0); + _bstr_t bstrName(name); + OLECHAR *names[1]; + names[0] = bstrName; + + DISPID dispatchID; + hr = pDispatch->GetIDsOfNames( + IID_NULL, names, 1, LOCALE_USER_DEFAULT, &dispatchID); + if (FAILED(hr)) { + Tcl_AppendResult( + interp, + "object does not implement method or property ", + name, + NULL); + return TCL_ERROR; + } + + UntypedArguments arguments; + int result = arguments.initialize( + interp, objc - 1, objv + 1, dispatchFlags); + if (result != TCL_OK) { + return result; + } + + // Set up return value. + _variant_t varReturnValue; + VARIANT *pReturnValue = + (dispatchFlags & DISPATCH_PROPERTYPUT) ? 0 : &varReturnValue; + + // Invoke method. + EXCEPINFO excepInfo; + memset(&excepInfo, 0, sizeof(excepInfo)); + + unsigned argErr; + hr = pDispatch->Invoke( + dispatchID, + IID_NULL, + LOCALE_USER_DEFAULT, + dispatchFlags, + arguments.dispParams(), + pReturnValue, + &excepInfo, + &argErr); + if (hr == DISP_E_EXCEPTION) { + // Clean up exception information strings. + _bstr_t source(excepInfo.bstrSource, false); + _bstr_t description(excepInfo.bstrDescription, false); + _bstr_t helpFile(excepInfo.bstrHelpFile, false); + + throw DispatchException(excepInfo.scode, description); + } + + if (FAILED(hr)) { + _com_issue_error(hr); + } + + if (pReturnValue != 0) { + TclObject returnValue(pReturnValue, Type::variant(), interp); + Tcl_SetObjResult(interp, returnValue); + } + return TCL_OK; +} + +// This Tcl command invokes a method or property on an interface pointer. + +static int +referenceObjCmd ( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + bool namedArgOpt = false; + WORD dispatchFlags = DISPATCH_METHOD | DISPATCH_PROPERTYGET; + + int i = 1; + for (; i < objc; ++i) { + static char *options[] = { + "-get", "-method", "-namedarg", "-set", NULL + }; + enum OptionEnum { + OPTION_GET, OPTION_METHOD, OPTION_NAMEDARG, OPTION_SET + }; + + int index; + if (Tcl_GetIndexFromObj(NULL, objv[i], options, "option", 0, &index) + != TCL_OK) { + break; + } + + switch (index) { + case OPTION_GET: + dispatchFlags = DISPATCH_PROPERTYGET; + break; + case OPTION_METHOD: + dispatchFlags = DISPATCH_METHOD; + break; + case OPTION_NAMEDARG: + namedArgOpt = true; + break; + case OPTION_SET: + dispatchFlags = DISPATCH_PROPERTYPUT; + break; + } + } + + if (objc - i < 1) { + Tcl_AppendResult( + interp, "usage: handle ?options? method ?arg ...?", NULL); + return TCL_ERROR; + } + + Reference *pReference = reinterpret_cast(clientData); + + int result; + try { + // Get interface description. + const Interface *pInterface = pReference->interfaceDesc(); + if (pInterface == 0) { + return invokeWithoutInterfaceDesc( + interp, pReference, objc - i, objv + i, dispatchFlags); + } + + const Method *pMethod; + const Property *pProperty; + const char *name = Tcl_GetStringFromObj(objv[i], 0); + + if ((pProperty = pInterface->findProperty(name)) != 0) { + // It's a property. + result = invokeProperty( + interp, + objc - i, + objv + i, + pReference, + pProperty); + + } else if ((pMethod = pInterface->findMethod(name)) != 0) { + // It's a method. + ++i; + NamedArguments namedArguments; + PositionalArguments positionalArguments; + TypedArguments *pArguments; + + if (namedArgOpt) { + pArguments = &namedArguments; + } else { + // Return an error if too many arguments were given. + const Method::Parameters ¶meters = pMethod->parameters(); + if (!pMethod->vararg() && objc - i > parameters.size()) { + wrongNumArgs(interp, objv + i - 1, parameters); + return TCL_ERROR; + } + pArguments = &positionalArguments; + } + result = pArguments->initialize( + interp, objc - i, objv + i, *pMethod, DISPATCH_METHOD); + if (result != TCL_OK) { + return result; + } + + result = invoke( + interp, + objc - i, + objv + i, + pReference, + pMethod, + namedArgOpt, + *pArguments, + DISPATCH_METHOD); + + } else { + Tcl_AppendResult( + interp, + "interface ", + pInterface->name().c_str(), + " does not have method or property ", + name, + NULL); + result = TCL_ERROR; + } + } + catch (_com_error &e) { + IErrorInfoPtr pErrorInfo; + if (getErrorInfo(pReference, &pErrorInfo)) { + BSTR descBstr; + pErrorInfo->GetDescription(&descBstr); + _bstr_t description(descBstr, false); + + result = setErrorCodeAndResult( + interp, e.Error(), description, __FILE__, __LINE__); + } else { + result = Extension::setComErrorResult( + interp, e, __FILE__, __LINE__); + } + } + catch (DispatchException &e) { + result = setErrorCodeAndResult( + interp, e.scode(), e.description(), __FILE__, __LINE__); + } + return result; +} + +// This command gets an interface pointer to an object identified by a moniker. + +static int +getObjectCmd ( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "monikerName"); + return TCL_ERROR; + } + + char *monikerName = Tcl_GetStringFromObj(objv[2], 0); + + try { + Reference *pReference = Reference::getObject(monikerName); + Tcl_SetObjResult( + interp, Extension::referenceHandles.newObj(interp, pReference)); + } + catch (_com_error &e) { + return Extension::setComErrorResult(interp, e, __FILE__, __LINE__); + } + return TCL_OK; +} + +// This command returns the reference count of an interface pointer. + +static int +countCmd( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "handle"); + return TCL_ERROR; + } + + Reference *pReference = Extension::referenceHandles.find(interp, objv[2]); + if (pReference == 0) { + char *arg = Tcl_GetStringFromObj(objv[2], 0); + Tcl_AppendResult( + interp, "invalid interface pointer handle ", arg, NULL); + return TCL_ERROR; + } + + IUnknown *pUnknown = pReference->unknown(); + pUnknown->AddRef(); + long count = pUnknown->Release(); + + Tcl_SetObjResult(interp, Tcl_NewLongObj(count)); + return TCL_OK; +} + +// This command compares two interface pointers for COM identity. + +static int +equalCmd( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "handle1 handle2"); + return TCL_ERROR; + } + + Reference *pReference1 = Extension::referenceHandles.find(interp, objv[2]); + if (pReference1 == 0) { + char *arg = Tcl_GetStringFromObj(objv[2], 0); + Tcl_AppendResult( + interp, "invalid interface pointer handle1 ", arg, NULL); + return TCL_ERROR; + } + + Reference *pReference2 = Extension::referenceHandles.find(interp, objv[3]); + if (pReference2 == 0) { + char *arg = Tcl_GetStringFromObj(objv[3], 0); + Tcl_AppendResult( + interp, "invalid interface pointer handle2 ", arg, NULL); + return TCL_ERROR; + } + + Tcl_SetObjResult( + interp, Tcl_NewBooleanObj(*pReference1 == *pReference2)); + return TCL_OK; +} + +// This command queries an interface pointer for an IDispatch interface. + +static int +queryDispatchCmd ( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "handle"); + return TCL_ERROR; + } + + Reference *pReference = Extension::referenceHandles.find(interp, objv[2]); + if (pReference == 0) { + char *arg = Tcl_GetStringFromObj(objv[2], (int *)0); + Tcl_AppendResult( + interp, "invalid interface pointer handle ", arg, NULL); + return TCL_ERROR; + } + + try { + Reference *pNewRef = Reference::queryInterface( + pReference->unknown(), IID_IDispatch); + Tcl_SetObjResult( + interp, + Extension::referenceHandles.newObj(interp, pNewRef)); + } + catch (_com_error &e) { + return Extension::setComErrorResult(interp, e, __FILE__, __LINE__); + } + return TCL_OK; +} + +// This command queries an interface pointer for a given interface. + +static int +queryInterfaceCmd ( + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "handle IID"); + return TCL_ERROR; + } + + Reference *pReference = Extension::referenceHandles.find(interp, objv[2]); + if (pReference == 0) { + char *arg = Tcl_GetStringFromObj(objv[2], (int *)0); + Tcl_AppendResult( + interp, "invalid interface pointer handle ", arg, NULL); + return TCL_ERROR; + } + + char *iidStr = Tcl_GetStringFromObj(objv[3], (int *)0); + IID iid; + if (UuidFromString(reinterpret_cast(iidStr), &iid) + != RPC_S_OK) { + Tcl_AppendResult( + interp, + "cannot convert to IID: ", + iidStr, + NULL); + return TCL_ERROR; + } + + try { + Reference *pNewRef = Reference::queryInterface( + pReference->unknown(), iid); + Tcl_SetObjResult( + interp, + Extension::referenceHandles.newObj(interp, pNewRef)); + } + catch (_com_error &e) { + return Extension::setComErrorResult(interp, e, __FILE__, __LINE__); + } + return TCL_OK; +} + +// This Tcl command gets a reference to an object. + +int +Extension::refCmd ( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand argument ..."); + return TCL_ERROR; + } + + Extension *pExtension = + static_cast(clientData); + pExtension->initializeCom(); + + static char *options[] = { + "count", + "createobject", + "equal", + "getactiveobject", + "getobject", + "querydispatch", + "queryinterface", + NULL + }; + enum SubCommandEnum { + COUNT, CREATEOBJECT, EQUAL, GETACTIVEOBJECT, GETOBJECT, QUERYDISPATCH, + QUERYINTERFACE + }; + + int subCommand; + if (Tcl_GetIndexFromObj(interp, objv[1], options, "subcommand", 0, + &subCommand) != TCL_OK) { + return TCL_ERROR; + } + + switch (subCommand) { + case COUNT: + return countCmd(interp, objc, objv); + case EQUAL: + return equalCmd(interp, objc, objv); + case GETOBJECT: + return getObjectCmd(interp, objc, objv); + case QUERYDISPATCH: + return queryDispatchCmd(interp, objc, objv); + case QUERYINTERFACE: + return queryInterfaceCmd(interp, objc, objv); + } + + bool clsIdOpt = false; + DWORD clsCtx = CLSCTX_SERVER; + + int i = 2; + for (; i < objc; ++i) { + static char *options[] = { + "-clsid", "-inproc", "-local", "-remote", NULL + }; + enum OptionEnum { + OPTION_CLSID, OPTION_INPROC, OPTION_LOCAL, OPTION_REMOTE + }; + + int index; + if (Tcl_GetIndexFromObj(NULL, objv[i], options, "option", 0, &index) + != TCL_OK) { + break; + } + + switch (index) { + case OPTION_CLSID: + clsIdOpt = true; + break; + case OPTION_INPROC: + clsCtx = CLSCTX_INPROC_SERVER; + break; + case OPTION_LOCAL: + clsCtx = CLSCTX_LOCAL_SERVER; + break; + case OPTION_REMOTE: + clsCtx = CLSCTX_REMOTE_SERVER; + break; + } + } + + if (i >= objc) { + Tcl_WrongNumArgs( + interp, + 2, + objv, + "?-clsid? ?-inproc? ?-local? ?-remote? progID ?hostName?"); + return TCL_ERROR; + } + + char *progId = Tcl_GetStringFromObj(objv[i], 0); + + char *hostName = (i + 1 < objc) ? Tcl_GetStringFromObj(objv[i + 1], 0) : 0; + if (clsCtx == CLSCTX_REMOTE_SERVER && hostName == 0) { + Tcl_AppendResult( + interp, "hostname required with -remote option", NULL); + return TCL_ERROR; + } + + try { + Reference *pReference; + + if (clsIdOpt) { + CLSID clsid; + if (UuidFromString( + reinterpret_cast(progId), &clsid) != RPC_S_OK) { + Tcl_AppendResult( + interp, + "cannot convert to CLSID: ", + progId, + NULL); + return TCL_ERROR; + } + pReference = (subCommand == GETACTIVEOBJECT) + ? Reference::getActiveObject(clsid, 0) + : Reference::createInstance(clsid, 0, clsCtx, hostName); + + } else { + pReference = (subCommand == GETACTIVEOBJECT) + ? Reference::getActiveObject(progId) + : Reference::createInstance(progId, clsCtx, hostName); + } + + Tcl_SetObjResult( + interp, + referenceHandles.newObj(interp, pReference)); + } + catch (_com_error &e) { + return setComErrorResult(interp, e, __FILE__, __LINE__); + } + return TCL_OK; +} diff --git a/src/resource.h b/src/resource.h new file mode 100644 index 0000000..c123d68 --- /dev/null +++ b/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by dllserver.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/shortPathNameCmd.cpp b/src/shortPathNameCmd.cpp new file mode 100644 index 0000000..a885e22 --- /dev/null +++ b/src/shortPathNameCmd.cpp @@ -0,0 +1,25 @@ +// $Id: shortPathNameCmd.cpp,v 1.3 2002/04/13 03:53:57 cthuang Exp $ +#include "Extension.h" +#define WIN32_LEAN_AND_MEAN +#include + +// This Tcl command returns the short path form of a input path. + +int +Extension::shortPathNameCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "inputPathName"); + return TCL_ERROR; + } + + char shortPath[MAX_PATH]; + GetShortPathName( + Tcl_GetStringFromObj(objv[1], 0), shortPath, sizeof(shortPath)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(shortPath, -1)); + return TCL_OK; +} diff --git a/src/tclRunTime.h b/src/tclRunTime.h new file mode 100644 index 0000000..4c89f3c --- /dev/null +++ b/src/tclRunTime.h @@ -0,0 +1,16 @@ +// $Id: tclRunTime.h,v 1.1 2002/07/15 04:03:54 cthuang Exp $ +#ifndef TCLRUNTIME_H +#define TCLRUNTIME_H + +#include + +// Link the Tcl run-time library. +#ifdef USE_TCL_STUBS +#pragma comment(lib, \ + "tclstub" STRINGIFY(JOIN(TCL_MAJOR_VERSION, TCL_MINOR_VERSION))) +#else +#pragma comment(lib, \ + "tcl" STRINGIFY(JOIN(TCL_MAJOR_VERSION, TCL_MINOR_VERSION))) +#endif + +#endif diff --git a/src/tcom.dsp b/src/tcom.dsp new file mode 100644 index 0000000..1672759 --- /dev/null +++ b/src/tcom.dsp @@ -0,0 +1,353 @@ +# Microsoft Developer Studio Project File - Name="tcom" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=tcom - Win32 No DCOM Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "tcom.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "tcom.mak" CFG="tcom - Win32 No DCOM Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "tcom - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "tcom - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "tcom - Win32 No DCOM Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "tcom - Win32 No DCOM Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "tcom - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TCOM_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /Od /I "\tcl\include" /D "NDEBUG" /D "_WIN32_DCOM" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TCOM_EXPORTS" /D "TCL_THREADS" /D "USE_TCL_STUBS" /D "USE_NON_CONST" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /libpath:"\tcl\lib" + +!ELSEIF "$(CFG)" == "tcom - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TCOM_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "\tcl\include" /D "_DEBUG" /D "_WIN32_DCOM" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TCOM_EXPORTS" /D "TCL_THREADS" /D "USE_TCL_STUBS" /D "USE_NON_CONST" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /libpath:"\tcl\lib" + +!ELSEIF "$(CFG)" == "tcom - Win32 No DCOM Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "tcom___Win32_No_DCOM_Release" +# PROP BASE Intermediate_Dir "tcom___Win32_No_DCOM_Release" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "No_DCOM_Release" +# PROP Intermediate_Dir "No_DCOM_Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /I "e:\tcl\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TCOM_EXPORTS" /D "_WIN32_DCOM" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "\tcl\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TCOM_EXPORTS" /D "TCL_THREADS" /D "USE_TCL_STUBS" /D "USE_NON_CONST" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 tk80.lib tcl80.lib rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /libpath:"e:\tcl\lib" +# ADD LINK32 rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /libpath:"\tcl\lib" + +!ELSEIF "$(CFG)" == "tcom - Win32 No DCOM Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "tcom___Win32_No_DCOM_Debug" +# PROP BASE Intermediate_Dir "tcom___Win32_No_DCOM_Debug" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "No_DCOM_Debug" +# PROP Intermediate_Dir "No_DCOM_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "c:\tcl\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TCOM_EXPORTS" /D "_WIN32_DCOM" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "\tcl\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TCOM_EXPORTS" /D "TCL_THREADS" /D "USE_TCL_STUBS" /D "USE_NON_CONST" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 tk80.lib tcl80.lib rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"c:\tcl\lib" +# ADD LINK32 rpcrt4.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /libpath:"\tcl\lib" + +!ENDIF + +# Begin Target + +# Name "tcom - Win32 Release" +# Name "tcom - Win32 Debug" +# Name "tcom - Win32 No DCOM Release" +# Name "tcom - Win32 No DCOM Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\Arguments.cpp +# End Source File +# Begin Source File + +SOURCE=.\bindCmd.cpp +# End Source File +# Begin Source File + +SOURCE=.\ComModule.cpp +# End Source File +# Begin Source File + +SOURCE=.\ComObject.cpp +# End Source File +# Begin Source File + +SOURCE=.\ComObjectFactory.cpp +# End Source File +# Begin Source File + +SOURCE=.\configureCmd.cpp +# End Source File +# Begin Source File + +SOURCE=.\Extension.cpp +# End Source File +# Begin Source File + +SOURCE=.\foreachCmd.cpp +# End Source File +# Begin Source File + +SOURCE=.\HandleSupport.cpp +# End Source File +# Begin Source File + +SOURCE=.\importCmd.cpp +# End Source File +# Begin Source File + +SOURCE=.\infoCmd.cpp +# End Source File +# Begin Source File + +SOURCE=.\InterfaceAdapter.cpp +# End Source File +# Begin Source File + +SOURCE=.\InterfaceAdapterVtbl.cpp +# End Source File +# Begin Source File + +SOURCE=.\main.cpp +# End Source File +# Begin Source File + +SOURCE=.\naCmd.cpp +# End Source File +# Begin Source File + +SOURCE=.\nullCmd.cpp +# End Source File +# Begin Source File + +SOURCE=.\objectCmd.cpp +# End Source File +# Begin Source File + +SOURCE=.\refCmd.cpp +# End Source File +# Begin Source File + +SOURCE=.\Reference.cpp +# End Source File +# Begin Source File + +SOURCE=.\RegistryKey.cpp +# End Source File +# Begin Source File + +SOURCE=.\shortPathNameCmd.cpp +# End Source File +# Begin Source File + +SOURCE=.\SupportErrorInfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\TclObject.cpp +# End Source File +# Begin Source File + +SOURCE=.\tcomVersion.rc +# End Source File +# Begin Source File + +SOURCE=.\TypeInfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\TypeLib.cpp +# End Source File +# Begin Source File + +SOURCE=.\typelibCmd.cpp +# End Source File +# Begin Source File + +SOURCE=.\Uuid.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\Arguments.h +# End Source File +# Begin Source File + +SOURCE=.\ComModule.h +# End Source File +# Begin Source File + +SOURCE=.\ComObject.h +# End Source File +# Begin Source File + +SOURCE=.\ComObjectFactory.h +# End Source File +# Begin Source File + +SOURCE=.\Extension.h +# End Source File +# Begin Source File + +SOURCE=.\HandleSupport.h +# End Source File +# Begin Source File + +SOURCE=.\HashTable.h +# End Source File +# Begin Source File + +SOURCE=.\InterfaceAdapter.h +# End Source File +# Begin Source File + +SOURCE=.\mutex.h +# End Source File +# Begin Source File + +SOURCE=.\Reference.h +# End Source File +# Begin Source File + +SOURCE=.\RegistryKey.h +# End Source File +# Begin Source File + +SOURCE=.\Singleton.h +# End Source File +# Begin Source File + +SOURCE=.\SupportErrorInfo.h +# End Source File +# Begin Source File + +SOURCE=.\TclObject.h +# End Source File +# Begin Source File + +SOURCE=.\tclRunTime.h +# End Source File +# Begin Source File + +SOURCE=.\tcomApi.h +# End Source File +# Begin Source File + +SOURCE=.\ThreadLocalStorage.h +# End Source File +# Begin Source File + +SOURCE=.\TypeInfo.h +# End Source File +# Begin Source File + +SOURCE=.\TypeLib.h +# End Source File +# Begin Source File + +SOURCE=.\Uuid.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/src/tcom.dsw b/src/tcom.dsw new file mode 100644 index 0000000..66a5795 --- /dev/null +++ b/src/tcom.dsw @@ -0,0 +1,74 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "TclScript"=.\TclScript.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name tcom + End Project Dependency +}}} + +############################################################################### + +Project: "dllserver"=.\dllserver.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name tcom + End Project Dependency +}}} + +############################################################################### + +Project: "exeserver"=.\exeserver.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name tcom + End Project Dependency +}}} + +############################################################################### + +Project: "tcom"=.\tcom.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/src/tcomApi.h b/src/tcomApi.h new file mode 100644 index 0000000..1cdd220 --- /dev/null +++ b/src/tcomApi.h @@ -0,0 +1,13 @@ +// $Id: tcomApi.h,v 1.1 2000/04/22 21:39:36 chuang Exp $ +#ifndef TCOMAPI_H +#define TCOMAPI_H + +#pragma warning(disable: 4251) + +#ifdef TCOM_EXPORTS +#define TCOM_API __declspec(dllexport) +#else +#define TCOM_API __declspec(dllimport) +#endif + +#endif diff --git a/src/tcomVersion.rc b/src/tcomVersion.rc new file mode 100644 index 0000000..12e742b --- /dev/null +++ b/src/tcomVersion.rc @@ -0,0 +1,35 @@ +// $Id: tcomVersion.rc,v 1.5 2002/04/27 18:15:24 cthuang Exp $ +#include +#include "version.h" +#include "buildNumber.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION PACKAGE_MAJOR_VERSION,PACKAGE_MINOR_VERSION,0,BUILD_NUMBER + PRODUCTVERSION PACKAGE_MAJOR_VERSION,PACKAGE_MINOR_VERSION,0,BUILD_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0 +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "tcom Tcl extension" + VALUE "FileVersion", PACKAGE_VERSION + VALUE "LegalCopyright", "Copyright 2002 by Chin Huang" + VALUE "OriginalFilename", "tcom.dll" + VALUE "ProductName", "tcom Tcl extension" + VALUE "ProductVersion", PACKAGE_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/typelibCmd.cpp b/src/typelibCmd.cpp new file mode 100644 index 0000000..b5da966 --- /dev/null +++ b/src/typelibCmd.cpp @@ -0,0 +1,256 @@ +// $Id: typelibCmd.cpp,v 1.29 2002/04/13 03:53:57 cthuang Exp $ +#pragma warning(disable: 4786) +#include "Extension.h" +#include "TypeLib.h" + +static int typeLibObjCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); +HandleSupport Extension::typeLibHandles(typeLibObjCmd); + +// Implement type library object command. + +static int +typeLibObjCmd ( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "method ?arg ...?"); + return TCL_ERROR; + } + + static char *options[] = { + "class", "documentation", "enum", "interface", "libid", "name", "version", NULL + }; + enum MethodEnum { + CLASS, DOCUMENTATION, ENUM, INTERFACE, LIBID, NAME, VERSION + }; + + int index; + if (Tcl_GetIndexFromObj(interp, objv[1], options, "method", 0, &index) + != TCL_OK) { + return TCL_ERROR; + } + + const TypeLib *pTypeLib = reinterpret_cast(clientData); + + switch (index) { + case CLASS: + if (objc == 2) { + // Get list of class names. + const TypeLib::Classes &classes = pTypeLib->classes(); + for (TypeLib::Classes::const_iterator p = classes.begin(); + p != classes.end(); ++p) { + Tcl_AppendElement( + interp, + const_cast(p->name().c_str())); + } + + } else if (objc == 3) { + // Get class description. + char *className = Tcl_GetStringFromObj(objv[2], 0); + + const Class *pClass = pTypeLib->findClass(className); + if (pClass == 0) { + Tcl_AppendResult( + interp, + "class not found: ", + className, + NULL); + return TCL_ERROR; + } + + // Append CLSID. + Tcl_AppendElement( + interp, + const_cast(pClass->clsidString().c_str())); + + // Append name of default interface. + Tcl_AppendElement( + interp, + const_cast( + pClass->defaultInterface()->name().c_str())); + + // Append name of source interface. + if (pClass->sourceInterface() != 0) { + Tcl_AppendElement( + interp, + const_cast( + pClass->sourceInterface()->name().c_str())); + } + + } else { + + Tcl_WrongNumArgs(interp, 2, objv, "?className?"); + return TCL_ERROR; + } + return TCL_OK; + + case DOCUMENTATION: + // Get type library documentation. + Tcl_AppendResult(interp, pTypeLib->documentation().c_str(), NULL); + return TCL_OK; + + case ENUM: + if (objc == 2) { + // Return list of enumerations. + const TypeLib::Enums &enums = pTypeLib->enums(); + for (TypeLib::Enums::const_iterator p = enums.begin(); + p != enums.end(); ++p) { + Tcl_AppendElement( + interp, + const_cast(p->name().c_str())); + } + + } else { + // Get the named enumeration. + char *name = Tcl_GetStringFromObj(objv[2], 0); + const Enum *pEnum = pTypeLib->findEnum(name); + if (pEnum == 0) { + Tcl_AppendResult(interp, "unknown enumeration ", name, NULL); + return TCL_ERROR; + } + + if (objc == 3) { + // Return list of enumerator name/value pairs. + for (Enum::const_iterator p = pEnum->begin(); p != pEnum->end(); + ++p) { + Tcl_AppendElement(interp, + const_cast(p->first.c_str())); + Tcl_AppendElement(interp, + const_cast(p->second.c_str())); + } + + } else if (objc == 4) { + // Return value of named enumerator. + char *name = Tcl_GetStringFromObj(objv[3], 0); + + Enum::const_iterator p = pEnum->find(name); + if (p == pEnum->end()) { + Tcl_AppendResult(interp, "unknown enumerator ", name, NULL); + return TCL_ERROR; + } + + Tcl_AppendElement(interp, + const_cast(p->second.c_str())); + + } else { + Tcl_WrongNumArgs( + interp, + 2, + objv, + "?enumerationName? ?enumeratorName?"); + return TCL_ERROR; + } + } + return TCL_OK; + + case INTERFACE: + if (objc == 2) { + // Get list of interface names. + const TypeLib::Interfaces &interfaces = pTypeLib->interfaces(); + for (TypeLib::Interfaces::const_iterator p = interfaces.begin(); + p != interfaces.end(); ++p) { + Tcl_AppendElement( + interp, + const_cast((*p)->name().c_str())); + } + + } else if (objc == 3) { + // Get interface description. + char *name = Tcl_GetStringFromObj(objv[2], 0); + + const Interface *pInterface = pTypeLib->findInterface(name); + if (pInterface == 0) { + Tcl_AppendResult( + interp, + "interface not found: ", + name, + NULL); + return TCL_ERROR; + } + + InterfaceHolder *pHolder = new InterfaceHolder(pInterface); + Tcl_Obj *pHandle = + Extension::interfaceHolderHandles.newObj(interp, pHolder); + Tcl_SetObjResult(interp, pHandle); + + } else { + Tcl_WrongNumArgs(interp, 2, objv, "?interfaceName?"); + return TCL_ERROR; + } + return TCL_OK; + + case LIBID: + Tcl_AppendResult(interp, pTypeLib->libidString().c_str(), NULL); + return TCL_OK; + + case NAME: + Tcl_AppendResult(interp, pTypeLib->name().c_str(), NULL); + return TCL_OK; + + case VERSION: + Tcl_AppendResult(interp, pTypeLib->version().c_str(), NULL); + return TCL_OK; + } + + return TCL_ERROR; +} + +// This Tcl command loads a type library. + +int +Extension::typelibCmd ( + ClientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[]) +{ + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "option typeLibrary"); + return TCL_ERROR; + } + + static char *options[] = { + "load", "register", "unregister", NULL + }; + enum SubCommandEnum { + LOAD, REGISTER, UNREGISTER + }; + + int index; + if (Tcl_GetIndexFromObj(interp, objv[1], options, "subcommand", 0, &index) + != TCL_OK) { + return TCL_ERROR; + } + + char *typeLibName = Tcl_GetStringFromObj(objv[2], 0); + + try { + TypeLib *pTypeLib; + + switch (index) { + case LOAD: + pTypeLib = TypeLib::load(typeLibName); + Tcl_SetObjResult( + interp, + typeLibHandles.newObj(interp, pTypeLib)); + break; + + case REGISTER: + pTypeLib = TypeLib::load(typeLibName, true); + delete pTypeLib; + break; + + case UNREGISTER: + TypeLib::unregister(typeLibName); + break; + } + } + catch (_com_error &e) { + return setComErrorResult(interp, e, __FILE__, __LINE__); + } + + return TCL_OK; +} diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..090cd68 --- /dev/null +++ b/src/version.h @@ -0,0 +1,14 @@ +// $Id: version.h,v 1.3 2002/04/27 18:15:24 cthuang Exp $ +#ifndef VERSION_H +#define VERSION_H + +#define PACKAGE_MAJOR_VERSION 3 +#define PACKAGE_MINOR_VERSION 8 + +#define MAKE_VERSION_STRING0(MAJOR,MINOR) #MAJOR "." #MINOR +#define MAKE_VERSION_STRING(MAJOR,MINOR) MAKE_VERSION_STRING0(MAJOR,MINOR) + +#define PACKAGE_VERSION \ + MAKE_VERSION_STRING(PACKAGE_MAJOR_VERSION,PACKAGE_MINOR_VERSION) + +#endif diff --git a/tests/all.tcl b/tests/all.tcl new file mode 100644 index 0000000..b2d4f9e --- /dev/null +++ b/tests/all.tcl @@ -0,0 +1,20 @@ +# $Id: all.tcl,v 1.1 2002/03/16 04:53:17 cthuang Exp $ +# +# This file contains a top-level script to run all of the tests. + +if {[lsearch [namespace children] ::tcltest] == -1} { + package require tcltest + namespace import -force ::tcltest::* +} + +set ::tcltest::testSingleFile false +set ::tcltest::testsDirectory [file dir [info script]] + +foreach file [::tcltest::getMatchingFiles] { + if {[catch {source $file} msg]} { + puts stdout $msg + } +} + +::tcltest::cleanupTests 1 +return diff --git a/tests/foreach.test b/tests/foreach.test new file mode 100644 index 0000000..55ea329 --- /dev/null +++ b/tests/foreach.test @@ -0,0 +1,42 @@ +# $Id: foreach.test,v 1.1 2002/03/16 04:53:17 cthuang Exp $ +# +# This file contains tests for the ::tcom::foreach command. + +if {[lsearch [namespace children] ::tcltest] == -1} { + package require tcltest + namespace import -force ::tcltest::* +} + +test foreach-1.1 {::tcom::foreach} { + package require tcom + + set application [::tcom::ref createobject "Excel.Application"] + $application Visible 1 + + set workbooks [$application Workbooks] + set workbook [$workbooks Add] + set worksheets [$workbook Worksheets] + set worksheet [$worksheets Item [expr 1]] + + set cells [$worksheet Cells] + set i 0 + foreach row {1 2 3} { + foreach column {A B C} { + $cells Item $row $column [incr i] + } + } + + set cellCount 0 + set range [$worksheet Range "A1:C3"] + ::tcom::foreach cell $range { + incr cellCount + } + + $workbook Saved 1 + $application Quit + + set cellCount +} {9} + +::tcltest::cleanupTests +return diff --git a/tests/namedarg.test b/tests/namedarg.test new file mode 100644 index 0000000..6b461d6 --- /dev/null +++ b/tests/namedarg.test @@ -0,0 +1,49 @@ +# $Id: namedarg.test,v 1.1 2002/06/21 02:38:50 cthuang Exp $ +# +# This file contains tests invoking methods through IDispatch with named +# arguments. + +if {[lsearch [namespace children] ::tcltest] == -1} { + package require tcltest + namespace import -force ::tcltest::* +} + +test namedarg-1.1 {named arguments, ChartWizard} { + package require tcom + + set application [::tcom::ref createobject "Excel.Application"] + $application Visible 1 + + set workbooks [$application Workbooks] + set workbook [$workbooks Add] + set worksheets [$workbook Worksheets] + set worksheet [$worksheets Item [expr 1]] + + set cells [$worksheet Cells] + $cells Item 1 A "North" + $cells Item 1 B "South" + $cells Item 1 C "East" + $cells Item 1 D "West" + $cells Item 2 A 5.2 + $cells Item 2 B 10.0 + $cells Item 2 C 8.0 + $cells Item 2 D 20.0 + set sourceRange [$worksheet Range "A1" "D2"] + + set charts [$workbook Charts] + set chart [$charts Add] + $chart -namedarg ChartWizard \ + Source $sourceRange \ + Gallery [expr -4102] \ + PlotBy [expr 1] \ + CategoryLabels [expr 1] \ + SeriesLabels [expr 0] \ + Title "Sales Percentages" + + # Prevent Excel from prompting to save the document on close. + $workbook Saved 1 + $application Quit +} {} + +::tcltest::cleanupTests +return diff --git a/tests/ref.test b/tests/ref.test new file mode 100644 index 0000000..bf28e22 --- /dev/null +++ b/tests/ref.test @@ -0,0 +1,52 @@ +# $Id: ref.test,v 1.2 2002/06/29 15:44:21 cthuang Exp $ +# +# This file contains tests for the ::tcom::ref command. + +if {[lsearch [namespace children] ::tcltest] == -1} { + package require tcltest + namespace import -force ::tcltest::* +} + +test createobject-1.1 {::tcom::ref createobject, Excel} { + package require tcom + + set application [::tcom::ref createobject "Excel.Application"] + $application Visible 1 + + set workbooks [$application Workbooks] + set workbook [$workbooks Add] + set worksheets [$workbook Worksheets] + set worksheet [$worksheets Item [expr 1]] + + set cells [$worksheet Cells] + set i 0 + foreach row {1 2 3} { + foreach column {A B C} { + $cells Item $row $column [incr i] + } + } + + $workbook Saved 1 + $application Quit +} {} + +test createobject-1.2 {::tcom::ref createobject, Banking example server} { + package require tcom + + set bank [::tcom::ref createobject "Banking.Bank"] + set account [$bank CreateAccount] + $account Deposit 30 + $account Withdraw 20 + $account Balance +} {10} + +test getobject-1.1 {::tcom::ref getobject, ADSI} { + package require tcom + + set computerName $env(COMPUTERNAME) + set object [::tcom::ref getobject "WinNT://$computerName,computer"] + $object Class +} {Computer} + +::tcltest::cleanupTests +return -- 2.23.0