**tcom server** Use [tcom] as an Active-X server, e.g. create com objects with tcl. Table of contents: <> ---- [http://www.vex.net/~cthuang/tcom/server.html%|%Original documentation%|%] ---- There are two connection methods to propose a com object to other applications: ***Running object*** The server aplication is already running and creates a com object without and client contact. The com object is registered in the running object table (ROT) of windows. This table is not contained in the registry but in memory. Registry entries are only used for helper issues. TCOM clients contact those objects using [http://www.vex.net/~cthuang/tcom/tcom.n.html%|%getactiveobject%|%]: ***Server object*** The TCL server application is started by the client and may be incorporated as DLL in the same process as the client or might be in another process. The registry is used to inform the client application about the location of the com server. TCOM clients contact those objects using [http://www.vex.net/~cthuang/tcom/tcom.n.html%|%createobject%|%]. Here are three variants: ****Inproc**** The COM object server is loaded as a DLL in the same process as the client application. ****Local**** The COM object server is loaded in another process. It might be an exe or a DLL. ****Remote**** The COM object server is loaded on another machine than the client program. It might be an exe or a DLL. The server seams to be identical. The COM machinery will transport the information. ---- The server may be a TCL script invoked by the tcl dll library or a wrapped exe file. Here is an overview, which method might be with which file type: %||Running object|Inproc Server object DLL|Local or Remote Server object |% &|TCL script|yes|yes|yes|& &|Wrapped exe|yes|no|yes|& **TCL script** The [http://www.vex.net/~cthuang/tcom/server.html%|%original documentation%|%] describes this implementation method. [Chin Huang] explained to [Jeff Godfrey] on [http://groups.google.com/group/comp.lang.tcl/browse_thread/thread/6f6ecd7ca12686b7%|%clt%|%] what really happens in the command '''http://www.vex.net/~cthuang/tcom/server.html''': ======none The tcom COM server implementation starts up a Tcl interpreter for each COM server, so I don't see how it can invoke the code in a wrapped application. The Tcl command ::tcom::server register Banking.tlb creates the Windows registry key HKEY_CLASSES_ROOT\\CLSID\\$clsid\\tcom with two values: "TclDLL" contains the full path to the Tcl interpreter DLL to load. "Script" contains Tcl code which the Tcl interpreter will execute to load and register the Tcl implementation of that COM class. The script is simply "package require Banking" because the Banking package encapsulates the COM class implementation. ====== ---- <> wrapped COM server [HaO] 2012-08-14: I want to build an active-x server application with a wrapped exe. On [http://groups.google.com/group/comp.lang.tcl/browse_thread/thread/6f6ecd7ca12686b7%|%clt%|%] answered [Chin Huang] to [Jeff Godfrey], that this might be not possible. Here is the key post: ======none I recently traded a few emails with tcom's author (Chin Huang) regarding this issue. Ultimately, using tcom to create a *wrapped* COM object doesn't seem possible. I've included some excerpts from our conversation below in case someone has further interest in the details. Or, maybe someone has some ideas for making this work? Thanks, Jeff Godfrey ===================================== -- Jeff to Chin -- 1. Is it possible to create a wrapped COM object using TCOM? 2. Would you happen to have any specific guidance in this regard, or have any example code? -- Chin to Jeff -- The tcom COM server implementation starts up a Tcl interpreter for each COM server, so I don't see how it can invoke the code in a wrapped application. The Tcl command ::tcom::server register Banking.tlb creates the Windows registry key HKEY_CLASSES_ROOT\\CLSID\\$clsid\\tcom with two values: "TclDLL" contains the full path to the Tcl interpreter DLL to load. "Script" contains Tcl code which the Tcl interpreter will execute to load and register the Tcl implementation of that COM class. The script is simply "package require Banking" because the Banking package encapsulates the COM class implementation. -- Jeff to Chin -- Not knowing much about it, it would seem that it might be possible to modify the values stored in the 2 mentioned registry entries in order to get COM to do the right thing in the face of a wrapped COM object. -- Chin to Jeff -- The COM server starts a Tcl interpreter and uses it to execute the Tcl implementation of the COM class. Having never developed a wrapped application, I don't know how the COM server Tcl interpreter can access Tcl code residing inside a wrapped application. -- Jeff to Chin -- Normally, in a wrapped application (starpack style), the application EXE contains the Tcl-interp. That get's bootstrapped by the EXE init process (details unknown to me) and then some "main" tcl procedure is called to start the wrapped application. So, the Tcl-app and the Tcl- interp are both in the wrapped EXE file. So, on the surface it would seem that "TclDLL" would point to the wrapped EXE itself and "Script" would point to the designated tcl procedure within the wrapped EXE. That said, I'm sure the devil is in the details and there are probably a number of stumbling blocks. -- Chin to Jeff -- You're going to have trouble here. The COM server specifically wants to load a DLL, not an EXE, containing the Tcl interpreter. It first tries to load the DLL at the path specified by "TclDLL", and failing that, tries to load the Tcl interpreter DLL installed by the ActiveState ActiveTcl installer. -- Jeff to Chin -- Understood - thanks. I guess I'll have to tackle this project using .NET... :-( ====== I am currently working on the issue. It will obviously not work as it is implemented. But there exists the possibility to use an Active X com exe server (out of proc server). So far, I did not find any information about those, expect that the register with the command line switch '''/regserver'''. See below about an example which works with a pre-created object. I suppose, there are some registry entries necessary to fulfill the task. <> <> Out of proc server with running object A running application may create itself a com object as a server and propose this object to clients. The corresponding client code uses '''::tcom::ref getactiveobject''' where examples may be found above. Those objects are listed in the [http://www.thrysoee.dk/InsideCOM+/ch11g.htm%|%running object table (ROT)%|%]. Here are my steps to create such a server: ***Preface*** The com utilities must be present. One possibility is to install Visual C express. All used command line utilities should be included there. ***Create a type library*** To create a type library, first an interface definition file must be written. An example is below. Its properties in descending logical order are: * File name: com_link_process.idl * Library name: Application * Object class: Step (coclass clause) * Interface name: IStep * Method name: Process * Input parameter: pIn of type `binary string pointer` (BSTR *). * return value: pRet of type `binary string pointer` (BSTR *). ======none import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(0C7E66F0-B50E-4B03-B784-2C89A9069B65), dual, helpstring("COM Link Step Interface"), pointer_default(unique) ] interface IStep: IDispatch { [id(1), helpstring("COM Link Process")] HRESULT Process( [in] BSTR *pIn, [out, retval] BSTR *pRet); }; [ uuid(16A8EC94-8E85-4D72-9425-B0C64E3BF6A8), version(1.0), helpstring("COM Link Process 1.0 Type Library") ] library Application { importlib("stdole32.tlb"); [ uuid(A7CB39F0-4996-4A43-AD5A-1C718D41CB98), helpstring("COM Link Step Class") ] coclass Step { [default] interface IStep; }; }; ====== ****uuid**** An '''uuid''' is required for the identification of all items. Those may be generated by: ======none C:\test>uuidgen 09e8f1b9-5d1a-409e-9a07-bba3dfbbdf5c ====== I experienced that it may be helpful to put them in uppercase. ****generate type library file**** To generate a type library file (.tlb) from an interface definition file, one may use `midl`: ======none C:\test>midl com_link_process.idl ====== The generated file '''com_link_processs.tlb''' must be copied to the server project files. In this example, it is copied in the same folder as the wrapped server executable. Thus, the file name is: ====== set Filename [file join [file dirname [info nameofexecutable]] com_link_process.idl] ====== ***Load type library*** The first step of the com server script is to import the type library: ====== if {[catch { package require tcom ::tcom::import $Filename } errMsg]} { tk_messageBox -message "Type library file '$Filename' missing.\n$Err" exit } ====== This creates the command '''::Application::Step'''. ***Create object*** Then, the exposed object of the server may be created: ====== set objectHandle [::tcom::object create -registeractive ::Application::Step COMStep] ====== The procedure '''COMStep''' is invoked, if a client invokes the method '''Process''' of the step. The MS-Visual tools contain a program '''ROT Viewer'''. The object may now be seen there by its class uuid: "A7CB39F0-4996-4A43-AD5A-1C718D41CB98". ***Object handling procedure*** Each call to the object invokes the object handling procedure '''COMStep'''. ====== proc COMStep {method args} { switch -exact -- $method { Process { return "[lindex $args 0] ok" } default { return -code error "Unknown method '$method'" } } } ====== ***Invoke the object by its class id*** Another interpreter may now access the object by its class uuid and invoke the process method: ====== % package require tcom % set h [::tcom::ref getactiveobject -clsid A7CB39F0-4996-4A43-AD5A-1C718D41CB98] ::tcom::handle0x02715AA9 % $h Process A A OK ====== ***Register Program ID*** To use the program ID '''Link3.Application''' instead the class ID, the following registry keys are necessary to set: ====== package require registry registry set {HKEY_LOCAL_MACHINE\SOFTWARE\Classes} Link3.Application "EasySoft.Link" registry set {HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Link3.Application} CLSID "{A7CB39F0-4996-4A43-AD5A-1C718D41CB98}" ====== The first key must be of the format [http://msdn.microsoft.com/en-us/library/ee487753.aspx%|%vendor.component%|%]. An included space resulted in the windows api function '''CLSIDFromProgID''' to return an error. This must be done as administrator or may be done by the installation routine. Note: I suppose from [http://msdn.microsoft.com/en-us/library/csw15z97.aspx], that this may not be done using the class property '''ProgID''' of the idl-file. I am not shure, if a specified/repeated ProgID within the idl file is of any use. ***Invoke the object by its program id*** Another interpreter may now access the object by its program id and invoke the Process method: ====== % package require tcom % set h [::tcom::ref getactiveobject Link3.Application] ::tcom::handle0x02715AA8 % $h Process A A OK ====== <> <> Wrapped out of proc server (work in progress) [HaO] 2012-08-22: Starting from the upper example: When I set the following registry keys: ====== registry set {HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID} "{A7CB39F0-4996-4A43-AD5A-1C718D41CB98}" Link3 registry set {HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{A7CB39F0-4996-4A43-AD5A-1C718D41CB98}}\ LocalServer32 {C:\oehhar\elmicron\projekte\el1043_elmiprint\elmiprint.exe} registry set {HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{A7CB39F0-4996-4A43-AD5A-1C718D41CB98}}\ ProgID Link3.Application ====== and I add a registerfactory call: ====== set objectHandle [::tcom::object registerfactory ::Application::Step COMStep] ====== and now, I call it from another instance: ====== % set h [::tcom::ref createobject Link3.Application] ::tcom::handle0x02930090 % $h Process a ====== The server will be started with the command line switch '''-Embedding'''. The '''Filename''' in ====== ::tcom::import $Filename ====== may not be within a virtual file system (e.g. in the wrapped application). It must first be copied outside of the vfs, as done with dlls. <> <> COM | Example | Package | Windows