From: Pat Thoyts Date: Fri, 13 Jun 2008 16:30:39 +0000 (+0100) Subject: optcl-3010 import X-Git-Url: https://privyetmir.co.uk/gitweb.cgi?a=commitdiff_plain;ds=inline;p=optcl optcl-3010 import --- diff --git a/GNU_Public_Licence.html b/GNU_Public_Licence.html deleted file mode 100644 index ee2d7f1..0000000 --- a/GNU_Public_Licence.html +++ /dev/null @@ -1,525 +0,0 @@ - - - -GNU General Public License - GNU Project - Free Software Foundation (FSF) - - - -

GNU General Public License

- - -

- -


- -

- -

Table of Contents

- - -

- -


- -

- - - -

GNU GENERAL PUBLIC LICENSE

-

-Version 2, June 1991 - -

- -
-Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
-59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
-
-Everyone is permitted to copy and distribute verbatim copies
-of this license document, but changing it is not allowed.
-
- - - -

Preamble

- -

- The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - -

-

- When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - -

-

- To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - -

-

- For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - -

-

- We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - -

-

- Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - -

-

- Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - -

-

- The precise terms and conditions for copying, distribution and -modification follow. - -

- - -

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

- - -

- -0. - This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". -

- -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - -

- -1. - You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. -

- -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. -

- -2. - You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: -

- -

- -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. -

- -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. -

- -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - -

- -3. - You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - - - -

- -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. -

- -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. -

- -4. - You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - -

- -5. - You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - -

- -6. - Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - -

- -7. - If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. -

- -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. -

- -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. -

- -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - -

- -8. - If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - -

- -9. - The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. -

- -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - -

- - -10. - If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - - -

NO WARRANTY

- -

- -11. - BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - -

- -12. - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - -

- - -

END OF TERMS AND CONDITIONS

- - - -

How to Apply These Terms to Your New Programs

- -

- If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - -

-

- To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - -

- -
-one line to give the program's name and an idea of what it does.
-Copyright (C) yyyy  name of author
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-
- -

-Also add information on how to contact you by electronic and paper mail. - -

-

-If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - -

- -
-Gnomovision version 69, Copyright (C) yyyy name of author
-Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
-type `show w'.  This is free software, and you are welcome
-to redistribute it under certain conditions; type `show c' 
-for details.
-
- -

-The hypothetical commands `show w' and `show c' should show -the appropriate parts of the General Public License. Of course, the -commands you use may be called something other than `show w' and -`show c'; they could even be mouse-clicks or menu items--whatever -suits your program. - -

-

-You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - -

- -
-Yoyodyne, Inc., hereby disclaims all copyright
-interest in the program `Gnomovision'
-(which makes passes at compilers) written 
-by James Hacker.
-
-signature of Ty Coon, 1 April 1989
-Ty Coon, President of Vice
-
- -

-This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. - -


- -Return to GNU's home page. -

-FSF & GNU inquiries & questions to -gnu@gnu.org. -Other ways to contact the FSF. -

-Comments on these web pages to -webmasters@www.gnu.org, -send other questions to -gnu@gnu.org. -

-Copyright notice above.
-Free Software Foundation, Inc., -59 Temple Place - Suite 330, Boston, MA 02111, USA -

-Updated: - -16 Feb 1998 tower - -


- - diff --git a/Images/class.gif b/Images/class.gif new file mode 100644 index 0000000..5b9ab9e Binary files /dev/null and b/Images/class.gif differ diff --git a/Images/copy.gif b/Images/copy.gif new file mode 100644 index 0000000..849f210 Binary files /dev/null and b/Images/copy.gif differ diff --git a/Images/dispatch.gif b/Images/dispatch.gif new file mode 100644 index 0000000..3f6fe40 Binary files /dev/null and b/Images/dispatch.gif differ diff --git a/Images/down.xbm b/Images/down.xbm new file mode 100644 index 0000000..c867fce --- /dev/null +++ b/Images/down.xbm @@ -0,0 +1,6 @@ +/* Created with The GIMP */ +#define C__Program_Files_Tcl_lib_width 12 +#define C__Program_Files_Tcl_lib_height 12 +static unsigned char C__Program_Files_Tcl_lib_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x00, 0xe0, 0x00, 0xf0, 0x00, + 0xe0, 0x00, 0xc0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/Images/enum.gif b/Images/enum.gif new file mode 100644 index 0000000..7e48ce0 Binary files /dev/null and b/Images/enum.gif differ diff --git a/Images/find.gif b/Images/find.gif new file mode 100644 index 0000000..4802a6c Binary files /dev/null and b/Images/find.gif differ diff --git a/Images/find.xbm b/Images/find.xbm new file mode 100644 index 0000000..03267a1 --- /dev/null +++ b/Images/find.xbm @@ -0,0 +1,9 @@ +/* Created with The GIMP */ +#define C__Program_Files_Tcl_lib_width 17 +#define C__Program_Files_Tcl_lib_height 18 +static unsigned char C__Program_Files_Tcl_lib_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x1c, 0x00, 0x50, 0x1c, 0x00, + 0x50, 0x1c, 0x00, 0x70, 0x1c, 0x00, 0xf8, 0x3e, 0x00, 0xe8, 0x3a, 0x00, + 0xfc, 0x7f, 0x00, 0x7e, 0xfb, 0x00, 0x7e, 0xfb, 0x00, 0xfe, 0xfb, 0x00, + 0xfe, 0xfe, 0x00, 0x3a, 0xe8, 0x00, 0x3a, 0xe8, 0x00, 0x3e, 0xf8, 0x00, + 0x3e, 0xf8, 0x00, 0x00, 0x00, 0x00 }; diff --git a/Images/hide.xbm b/Images/hide.xbm new file mode 100644 index 0000000..11e2008 --- /dev/null +++ b/Images/hide.xbm @@ -0,0 +1,9 @@ +/* Created with The GIMP */ +#define C__Program_Files_Tcl_lib_width 17 +#define C__Program_Files_Tcl_lib_height 17 +static unsigned char C__Program_Files_Tcl_lib_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x40, 0x02, 0x00, 0x20, 0x04, 0x00, 0x10, 0x08, 0x00, + 0x80, 0x01, 0x00, 0x40, 0x02, 0x00, 0x20, 0x04, 0x00, 0x10, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; diff --git a/Images/interface.gif b/Images/interface.gif new file mode 100644 index 0000000..40548e6 Binary files /dev/null and b/Images/interface.gif differ diff --git a/Images/libselect.gif b/Images/libselect.gif new file mode 100644 index 0000000..ded2e77 Binary files /dev/null and b/Images/libselect.gif differ diff --git a/Images/libselect.xbm b/Images/libselect.xbm new file mode 100644 index 0000000..4602777 --- /dev/null +++ b/Images/libselect.xbm @@ -0,0 +1,9 @@ +/* Created with The GIMP */ +#define c__program_files_tcl_lib_width 17 +#define c__program_files_tcl_lib_height 17 +static unsigned char c__program_files_tcl_lib_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0xf4, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; diff --git a/Images/method.gif b/Images/method.gif new file mode 100644 index 0000000..4835497 Binary files /dev/null and b/Images/method.gif differ diff --git a/Images/module.gif b/Images/module.gif new file mode 100644 index 0000000..09b5e0b Binary files /dev/null and b/Images/module.gif differ diff --git a/Images/noselect.gif b/Images/noselect.gif new file mode 100644 index 0000000..9623a07 Binary files /dev/null and b/Images/noselect.gif differ diff --git a/Images/property.gif b/Images/property.gif new file mode 100644 index 0000000..1e0af13 Binary files /dev/null and b/Images/property.gif differ diff --git a/Images/right.xbm b/Images/right.xbm new file mode 100644 index 0000000..9a8a5f0 --- /dev/null +++ b/Images/right.xbm @@ -0,0 +1,6 @@ +/* Created with The GIMP */ +#define c__program_files_tcl_lib_width 12 +#define c__program_files_tcl_lib_height 12 +static unsigned char c__program_files_tcl_lib_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00, + 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/Images/select.gif b/Images/select.gif new file mode 100644 index 0000000..9b6a0bf Binary files /dev/null and b/Images/select.gif differ diff --git a/Images/show.xbm b/Images/show.xbm new file mode 100644 index 0000000..e9d8548 --- /dev/null +++ b/Images/show.xbm @@ -0,0 +1,9 @@ +/* Created with The GIMP */ +#define C__Program_Files_Tcl_lib_width 17 +#define C__Program_Files_Tcl_lib_height 17 +static unsigned char C__Program_Files_Tcl_lib_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x20, 0x04, 0x00, 0x40, 0x02, 0x00, + 0x80, 0x01, 0x00, 0x10, 0x08, 0x00, 0x20, 0x04, 0x00, 0x40, 0x02, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; diff --git a/Images/struct.gif b/Images/struct.gif new file mode 100644 index 0000000..2b8c789 Binary files /dev/null and b/Images/struct.gif differ diff --git a/Images/typedef.gif b/Images/typedef.gif new file mode 100644 index 0000000..1faec02 Binary files /dev/null and b/Images/typedef.gif differ diff --git a/Images/union.gif b/Images/union.gif new file mode 100644 index 0000000..06e042f Binary files /dev/null and b/Images/union.gif differ diff --git a/ReadMe.txt b/ReadMe.txt index 310352f..b2ead57 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -1,19 +1,27 @@ -OpTcl v3.0 build 04 + +OpTcl v3.0 build 10 ------------------- +PRE PRE ALPHA RELEASE (Friends and Family only) + +What's New +---------- +Areas with Bug Fixes: Reference counting, Query Interfacing, (in/)out parameters; -with optimisation. + +New Features: Can now call custom interfaces. Handles COM's record structures. Library browser has search and history facilities. OpTcl works with COM better than ever before! :-) Licencing --------- -Use of this software indicates an agreement to the GNU Public Licence under which, -this software is provided. +Ignore any references to LGPL. I hereby renounce this license for one which is in-line with the BSD license. Please read the enclosed license.txt for details. Documentation ------------- -Please open the default.html file in the 'docs' directory for installation instructions -and documentation. +This is a beta release so the docs aren't there yet ... I've included the old ones just for comparison. A lot of the previous functionality and syntax is identical. A few things however have changed dramatically. +To start things off try the following +package require optcl +tlview::viewlib .l +I welcome any comments, suggestions and bug reports. Enjoy! -I welcome any comments, suggestions and bug reports: +Farzad. fuzz@sys.uea.ac.uk - - diff --git a/docs/optcl.html b/docs/optcl.html index 206a2d5..a1b76dc 100644 --- a/docs/optcl.html +++ b/docs/optcl.html @@ -3,7 +3,7 @@ - + OpTcl Documentation @@ -18,12 +18,8 @@ Pezeshkpour

August 1999

-

This software is freely distributed under the GNU Public -Licence. I've include this in this distribution as an HTML file.

- -

Gosh! So much to document here, and so little time. This is a -preliminary, and rather informal document - a better version is -on its way!

+

This software is freely distributed under the GNU Lesser General Public License. I've include this in this distribution as an + HTML file.

The Distribution

@@ -33,9 +29,11 @@ the distribution:

- + @@ -49,7 +47,8 @@ the distribution:

+ Calendar Control. Need to put an example of scripting DirectX - should + nicely show off what OpTcl can really do! :-)
installHolds the installer script and two versions of the - DLL - one for Tcl 8.0.5 (no stubs), and one with stubs - enabled, built for Tcl 8.2 libraries.Holds the installer script and the debug and release versions of the + stubbed DLLs for Tcl8.2 and above. I also wanted to build a + distribution for Tcl8.0.5+ without stubs, but no matter were I + downloaded the Tcl installer from, it failed half way through the + installation.
docs
tests A couple of test scripts using MS Word, and the - Calendar Control.
@@ -69,15 +68,14 @@ is applied to that directory.

  • Distribute example files.
  • Implement array parameters.
  • Implement default parameter values.
  • -
  • Test out-flagged parameters (I haven't found a - server to do this, yet).
  • Implement in/out-flagged parameters for events.
  • Use Type Libraries on the type conversion of an event-handlers result.
  • Test with DCOM. (does anyone have a setup that can do this?)
  • Write an ActiveScripting engine interface.
  • -
  • In some cases perhaps, reconsider syntax.
  • +
  • Remove explicit reference counting - I can't do this until Scriptics + patch the Tcl core to enable 'precious' objects.
  • Known Bugs/Limitations

    @@ -87,6 +85,8 @@ is applied to that directory.

  • Can't set parameters by their name, à la Visual Basic.
  • Microsoft Forms ActiveX objects can't be fully in-place activated.
  • +
  • Lack of reference counting means that objects within records need to be + explicitly unlocked.
  • Credits

    diff --git a/docs/optcltypelibaccess.html b/docs/optcltypelibaccess.html index f83e0b8..7527b88 100644 --- a/docs/optcltypelibaccess.html +++ b/docs/optcltypelibaccess.html @@ -41,7 +41,6 @@ synopsis:

    libname
    typelib::typeinfo libname.type ?element?
    -
     

    Description

    diff --git a/docs/optcltypes.html b/docs/optcltypes.html index 55bcdb6..54dd225 100644 --- a/docs/optcltypes.html +++ b/docs/optcltypes.html @@ -27,10 +27,11 @@ accurrately described.

    allow for the accurate type conversion between Tcl objects and COM types, and b) speed up an invocation on an object.

    -

    Optcl is now type library aware - at least in terms of OpTcl is now type library aware - at least in terms of reading them and being type -sensitive in its invocations. I hope a future version will be -able to write out Type Libraries.

    +sensitive in its invocations. I have a test version that can write out new +libraries, and wrap Tcl scripts as fully-fledged ActiveX objects - more about +this later...

    In OpTcl, types are represented as a strings comprising of the programmatic name for a type library and its contained typename, diff --git a/install/optcl80.dll b/install/optcl80.dll deleted file mode 100644 index a45c51a..0000000 Binary files a/install/optcl80.dll and /dev/null differ diff --git a/install/optcl_Install.tcl b/install/optcl_Install.tcl deleted file mode 100644 index 0b04ace..0000000 --- a/install/optcl_Install.tcl +++ /dev/null @@ -1,88 +0,0 @@ - -# OpTcl Installer -# Author: Fuzz -# fuzz@sys.uea.ac.uk - -package require registry - -set piccy ../docs/optcl_medium.gif - -set installfolder [file join [info library] .. optcl] -set installname optcl.dll - -puts "Install dir: $installfolder" -set version [info tclversion] - -if {$version < 8.0} { - tk_messageBox -message "Sorry, but OpTcl needs Tcl version 8.0.5" -type ok - exit -} elseif {$version < 8.1} { - set dll optcl80.dll -} elseif {$version < 9.0} { - set dll optclstubs.dll -} else { - tk_messageBox -message "Sorry, but OpTcl was compiled for Tcl major-version 8" -type ok -} - -image create photo optclim -file $piccy - -proc updategui {} { - global installfolder installname - if [file exists [file join $installfolder $installname]] { - .uninstall config -state normal - .install config -text "Re-install for Tcl[info tclversion]" - } else { - .uninstall config -state disabled - .install config -text "Install for Tcl[info tclversion]" - } -} - -proc install {} { - global installfolder installname dll - set answer [tk_messageBox -title {} -message "Okay to install $dll in $installfolder\nand register as OpTcl package?" -icon question -type yesno] - - switch $answer { - no {} - yes { - set bad [catch { - file mkdir $installfolder - file copy -force $dll [file join $installfolder $installname] - pkg_mkIndex -direct $installfolder - } err] - if {$bad} { - tk_messageBox -type ok -message "Error: $err" -icon error - } else { - tk_messageBox -type ok -message "OpTcl successfully installed." -icon info - } - exit - } - } -} - -proc uninstall {} { - global installfolder installname - set reply [tk_messageBox -type yesno -message "Delete package OpTcl located at $installfolder?" -icon question] - if {[string compare $reply yes] != 0} return - file delete [file join $installfolder $installname] [file join $installfolder pkgIndex.tcl] $installfolder - updategui -} - -wm title . "OpTcl Installer - F2 for console" -bind . {console show} -bind . {exit} - -label .im -image optclim -relief flat -bd 0 -button .install -text Install... -command install -width 16 -height 1 -bd 2 -font {arial 8 bold} -button .uninstall -text Uninstall -command uninstall -width 16 -height 1 -bd 2 -font {arial 8 bold} -button .quit -text Quit -command exit -bd 2 -font {arial 8 bold} -width 5 -height 1 - -grid .im -column 0 -row 0 -rowspan 2 -padx 2 -pady 2 -grid .install -column 1 -row 0 -padx 2 -pady 2 -sticky nsew -grid .uninstall -column 2 -row 0 -padx 2 -pady 2 -sticky nsew -grid .quit -column 1 -row 1 -columnspan 2 -padx 2 -pady 2 -sticky nsew - - -wm resizable . 0 0 -updategui -raise . -focus -force . diff --git a/install/optclstubs.dll b/install/optclstubs.dll deleted file mode 100644 index 516434b..0000000 Binary files a/install/optclstubs.dll and /dev/null differ diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..363ea12 --- /dev/null +++ b/license.txt @@ -0,0 +1,38 @@ +This software is copyrighted by Farzad Pezeshkpour. +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. + +GOVERNMENT USE: If you are acquiring this software on behalf of the +U.S. government, the Government shall have only "Restricted Rights" +in the software and related documentation as defined in the Federal +Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you +are acquiring the software on behalf of the Department of Defense, the +software shall be classified as "Commercial Computer Software" and the +Government shall have only "Restricted Rights" as defined in Clause +252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the +authors grant the U.S. Government and others acting in its behalf +permission to use and distribute the software in accordance with the +terms specified in this license. diff --git a/src/ComRecordInfoImpl.cpp b/src/ComRecordInfoImpl.cpp new file mode 100644 index 0000000..d393911 --- /dev/null +++ b/src/ComRecordInfoImpl.cpp @@ -0,0 +1,1162 @@ + + +/* + *------------------------------------------------------------------------- + * ComRecordInfoImpl.cpp + * Implements an IRecordInfo, that unlike the one shipped by MS, isn't + * reliant on the presence of a GUID for any structure. + * Copyright (C) 2000 Farzad Pezeshkpour + * + * Email: fuzz@sys.uea.ac.uk + * Date: 6th April 2000 + * + * Licence: + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *------------------------------------------------------------------------- + */ + + + +#include "stdafx.h" +#include +#include +#include "ComRecordInfoImpl.h" + +/* + *------------------------------------------------------------------------- + * Class CComRecordInfoImpl -- + * Declaration of the class that implements the new IRecord Info. + *------------------------------------------------------------------------- + */ +class CComRecordInfoImpl : +public CComObjectRoot, public IRecordInfo +{ +public: + BEGIN_COM_MAP(CComRecordInfoImpl) + COM_INTERFACE_ENTRY(IRecordInfo) + END_COM_MAP() + + + CComRecordInfoImpl(); + virtual ~CComRecordInfoImpl(); + + HRESULT SetTypeInfo (ITypeInfo *pti); + void FinalRelease (); + + STDMETHOD(RecordInit)(PVOID pvNew); + STDMETHOD(RecordClear)(PVOID pvExisting); + STDMETHOD(RecordCopy)(PVOID pvExisting, PVOID pvNew); + STDMETHOD(GetGuid)(GUID *pguid); + STDMETHOD(GetName)(BSTR *pbstrName); + STDMETHOD(GetSize)(ULONG *pcbSize); + STDMETHOD(GetTypeInfo)(ITypeInfo * *ppTypeInfo); + STDMETHOD(GetField)(PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField); + STDMETHOD(GetFieldNoCopy)(PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray); + STDMETHOD(PutField)(ULONG wFlags, PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField); + STDMETHOD(PutFieldNoCopy)(ULONG wFlags, PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField); + STDMETHOD(GetFieldNames)(ULONG *pcNames, BSTR *rgBstrNames); + BOOL STDMETHODCALLTYPE IsMatchingType(IRecordInfo *pRecordInfo); + PVOID STDMETHODCALLTYPE RecordCreate(void); + STDMETHOD(RecordCreateCopy)(PVOID pvSource, PVOID *ppvDest); + STDMETHOD(RecordDestroy)(PVOID pvRecord); + +protected: + STDMETHODIMP GetFieldNoCopy(PVOID pvData, VARDESC *pvd, VARIANT *pvarField, PVOID *ppvDataCArray); + STDMETHODIMP PutFieldNoCopy(ULONG wFlags, PVOID pvData, VARDESC *pvd, VARIANT *pvarField); +protected: + void ReleaseTypeAttr (); + +protected: + CComPtr m_pti; // type info we're implementing + TYPEATTR * m_pta; // type attribute for the type + CComBSTR m_name; // name of the this record type +}; + + + +/* + *------------------------------------------------------------------------- + * Class: CVarDesc + * Implements a wrapper for the VARDESC data type, and its retrieval from + * an ITypeInfo interface pointer. + *------------------------------------------------------------------------- + */ +class CVarDesc { +protected: + CComPtr m_pti; // reference to the ITypeInfo parent of the VARDESC +public: + VARDESC * m_pvd; // the vardesc itself +public: + // constructor / destructor + CVarDesc () : m_pvd(NULL) {} + + virtual ~CVarDesc () { + Release(); + } + + + // operator overloads to make this object look more like a VARDESC... + + // pointer de-reference + VARDESC * operator-> () { + ATLASSERT (m_pvd != NULL); + return m_pvd; + } + + // castin operator + operator VARDESC* () { + ATLASSERT (m_pvd != NULL); + return m_pvd; + } + + /* + *------------------------------------------------------------------------- + * Release -- + * Releases the VARDESC if it has been allocated. + * Releases reference to the ITypeInfo. + * + * Result: + * None. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ + void Release () { + if (m_pvd != NULL) { + ATLASSERT(m_pti != NULL); + m_pti->ReleaseVarDesc(m_pvd); + m_pti.Release(); + m_pvd = NULL; + } + } + + + /* + *------------------------------------------------------------------------- + * Set -- + * Sets the VARDESC based on an index into the ITypeInfo parameter. + * + * Result: + * S_OK iff succeeded. + * + * Side Effects: + * Any previous VARDESC is released. + *------------------------------------------------------------------------- + */ + HRESULT Set (ITypeInfo *pti, ULONG index) { + Release(); + m_pti = pti; + HRESULT hr; + hr = m_pti->GetVarDesc (index, &m_pvd); + return hr; + } + + + /* + *------------------------------------------------------------------------- + * Set -- + * Sets the VARDESC based on the variable name within the ITypeInfo parameter. + * + * Result: + * S_OK iff succeeded. + * + * Side Effects: + * Any previous VARDESC is released. + *------------------------------------------------------------------------- + */ + HRESULT Set (ITypeInfo *pti, LPCOLESTR name) { + CComPtr ptc; + HRESULT hr; + hr = pti->GetTypeComp (&ptc); + if (FAILED(hr)) + return hr; + CComPtr pti2; + DESCKIND dk; + BINDPTR bp; + hr = ptc->Bind ((LPOLESTR)name, 0, INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF, &pti2, &dk, &bp); + if (FAILED(hr)) + return hr; + if (dk != DESCKIND_VARDESC) { + ReleaseBindPtr(dk, bp); + return E_FAIL; + } else { + Release(); + m_pvd = bp.lpvardesc; + m_pti = pti; + return S_OK; + } + } + + +private: + /* + *------------------------------------------------------------------------- + * ReleaseBindPtr -- + * Releases the bind ptr according to its type. + * + * Result: + * None. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ + void ReleaseBindPtr (DESCKIND dk, BINDPTR bp) { + if (bp.lptcomp == NULL) + return; + + switch (dk) { + case DESCKIND_FUNCDESC: + m_pti->ReleaseFuncDesc(bp.lpfuncdesc); + break; + case DESCKIND_TYPECOMP: + bp.lptcomp->Release(); + break; + default: + ATLASSERT(FALSE); + break; + } + } +}; + + +//------------------ IRecordInfo Implementation --------------------------- + + +/* + *------------------------------------------------------------------------- + * GetRecordInfoFromTypeInfo2 -- + * Creates a valid IRecordInfo interface for the give ITypeInfo interface. + * The only criteria is that the type info must be of the type TKIND_RECORD + * The type info does not have to provide a GUID. + * + * Result: + * S_OK iff successful. + * + * Side Effects: + * A CComRecordInfo object is created on the heap. + * + *------------------------------------------------------------------------- + */ +HRESULT GetRecordInfoFromTypeInfo2 (ITypeInfo *pti, IRecordInfo **ppri) +{ + ATLASSERT (pti != NULL && ppri != NULL); + CComObject *pri = NULL; + CComPtr ptmpri; + HRESULT hr = CComObject::CreateInstance (&pri); + if (FAILED (hr)) + return hr; + hr = pri->QueryInterface (&ptmpri); + if (FAILED(hr)) + return hr; + hr = pri->SetTypeInfo (pti); + if (FAILED (hr)) + return hr; + return ptmpri.CopyTo(ppri); +} + + + + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CComRecordInfoImpl::CComRecordInfoImpl() : m_pta(NULL) +{ + +} + +CComRecordInfoImpl::~CComRecordInfoImpl() +{ + +} + +/* + *------------------------------------------------------------------------- + * FinalRelease -- + * Called by the ATL framework when the object is about to be destroyed. + * + * Result: + * None. + * + * Side Effects: + * Releases the TYPEATTR for this Record Info. + *------------------------------------------------------------------------- + */ +void CComRecordInfoImpl::FinalRelease () { + ReleaseTypeAttr(); +} + + +/* + *------------------------------------------------------------------------- + * SetTypeInfo -- + * Sets the current TypeInfo that this RecordInfo is implementing. + * + * Result: + * S_OK iff successful. + * + * Side Effects: + * Releases any previous type info reference and attributes. + * + *------------------------------------------------------------------------- + */ +HRESULT CComRecordInfoImpl::SetTypeInfo (ITypeInfo *pti) +{ + TYPEATTR *pta = NULL; + // retrieve the type attribute for the + try { + if (FAILED(pti->GetTypeAttr(&pta))) + throw false; + if (pta->typekind != TKIND_RECORD) + throw false; + ReleaseTypeAttr(); + m_pti = pti; + m_pta = pta; + pti->GetDocumentation(-1, &m_name, NULL, NULL, NULL); + return S_OK; + } catch (...) { + if (pta != NULL) + pti->ReleaseTypeAttr(pta); + return E_INVALIDARG; + } +} + +/* + *------------------------------------------------------------------------- + * ReleaseTypeAttr -- + * Releases the TYPEATTR if any. + * + * Result: + * None. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +void CComRecordInfoImpl::ReleaseTypeAttr () +{ + ATLASSERT (m_pta == NULL || m_pti != NULL); + + if (m_pta != NULL && m_pti != NULL) { + m_pti->ReleaseTypeAttr(m_pta); + m_pta = NULL; + } +} + + + + + + +/* + *------------------------------------------------------------------------- + * RecordInit -- + * Initiliases the contents of a created record structure. All existing + * values are ignored. + * + * Result: + * S_OK iff successfull. + * + * Side Effects: + * None. + * + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::RecordInit(PVOID pvNew) +{ + HRESULT hr; + for (WORD iVar = 0; iVar < m_pta->cVars; iVar++) { + + CVarDesc vd; + PVOID pvField; + CComPtr pRefInfo; + CComPtr pRefRecInfo; + + + hr = vd.Set(m_pti, iVar); + if (FAILED(hr)) + return hr; + ATLASSERT ( (vd->elemdescVar.tdesc.vt & VT_BYREF) == NULL); + + + pvField = (BYTE*)pvNew + vd->oInst; + + + switch (vd->elemdescVar.tdesc.vt) { + case VT_USERDEFINED: + hr = m_pti->GetRefTypeInfo(vd->elemdescVar.tdesc.hreftype, &pRefInfo); + if (FAILED(hr)) return hr; + + hr = GetRecordInfoFromTypeInfo2 (pRefInfo, &pRefRecInfo); + if (FAILED(hr)) return hr; + + hr = pRefRecInfo->RecordInit(pvField); + if (FAILED(hr)) + return hr; + break; + + case VT_BSTR: + // is this correct? + *((BSTR*)pvField) = SysAllocString (L""); + break; + + case VT_DATE: + *((DATE*)pvField) = 0; + break; + + case VT_CY: + ((CY*)pvField)->int64 = 0; + break; + + // generic 8bit data types + case VT_I1: + case VT_UI1: + *((BYTE*)pvField) = 0; + break; + + // generic 16bit data types + case VT_I2: + case VT_UI2: + *((SHORT*)pvField) = 0; + break; + + // generic 32 bit data types + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_UNKNOWN: + case VT_DISPATCH: + case VT_ERROR: + *((ULONG*)pvField) = 0; + break; + + // platform specific: INT + case VT_INT: + case VT_UINT: + *((INT*)pvField) = 0; + break; + + // boolean + case VT_BOOL: + *((VARIANT_BOOL*)pvField) = VARIANT_FALSE; + break; + + // double + case VT_R8: + *((DOUBLE*)pvField) = double(0); + break; + + default: + // is it an array? + if (vd->elemdescVar.tdesc.vt & VT_ARRAY) { + *((SAFEARRAY**)pvField) = NULL; + } + } + + } + return S_OK; +} + + + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::RecordClear -- + * Iterates through the existing record, clearing all referenced resources, + * and setting to zero. + * + * Result: + * Standard COM result. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::RecordClear(PVOID pvExisting) +{ + HRESULT hr; + for (WORD iVar = 0; iVar < m_pta->cVars; iVar++) { + + CVarDesc vd; + PVOID pvField; + CComPtr pRefInfo; + CComPtr pRefRecInfo; + + + hr = vd.Set(m_pti, iVar); + if (FAILED(hr)) + return hr; + ATLASSERT ( (vd->elemdescVar.tdesc.vt & VT_BYREF) == NULL); + + pvField = (BYTE*)pvExisting + vd->oInst; + + if (vd->elemdescVar.tdesc.vt & VT_ARRAY) { + SafeArrayDestroy (*((SAFEARRAY**)pvField)); + *((SAFEARRAY**)pvField) = NULL; + } else { + switch (vd->elemdescVar.tdesc.vt) { + case VT_USERDEFINED: + hr = m_pti->GetRefTypeInfo(vd->elemdescVar.tdesc.hreftype, &pRefInfo); + if (FAILED(hr)) return hr; + + hr = GetRecordInfoFromTypeInfo2 (pRefInfo, &pRefRecInfo); + if (FAILED(hr)) return hr; + + hr = pRefRecInfo->RecordClear(pvField); + if (FAILED(hr)) + return hr; + break; + /* strings */ + case VT_BSTR: + SysFreeString(*( (BSTR*)pvField )); + *( (BSTR*)pvField ) = NULL; + break; + /* interface types */ + case VT_DISPATCH: + case VT_UNKNOWN: + (*((IUnknown**)pvField))->Release(); + (*((IUnknown**)pvField)) = NULL; + break; + } + } + } + return S_OK; +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::RecordCopy -- + * Makes a copy of the existing record to the new record. + * + * Result: + * Standard COM result. + * + * Side Effects: + * Performs a deep copy on all references. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::RecordCopy(PVOID pvExisting, PVOID pvNew) +{ + HRESULT hr; + for (WORD iVar = 0; iVar < m_pta->cVars; iVar++) { + PVOID pvSrc, pvDst; + CVarDesc vd; + CComPtr refInfo; + CComPtr refrecInfo; + + hr = vd.Set (m_pti, iVar); + if (FAILED(hr)) return hr; + + pvSrc = (BYTE*)pvExisting + vd->oInst; + pvDst = (BYTE*)pvNew + vd->oInst; + + ATLASSERT ( (vd->elemdescVar.tdesc.vt & VT_BYREF) == 0); + if (vd->elemdescVar.tdesc.vt & VT_ARRAY != 0) { + hr = SafeArrayCopyData (*((SAFEARRAY**)pvSrc), *((SAFEARRAY**)pvDst)); + if (FAILED(hr)) return hr; + } else { + switch (vd->elemdescVar.tdesc.vt) { + // interfaces ... + case VT_UNKNOWN: + case VT_DISPATCH: + *((IUnknown**)pvDst) = *((IUnknown**)pvSrc); + (*((IUnknown**)pvDst))->AddRef(); + break; + // string + case VT_BSTR: + *((BSTR*)pvDst) = SysAllocString (*((BSTR*)pvSrc)); + break; + // 8 bit copy + case VT_I1: + case VT_UI1: + *((BYTE*)pvDst) = *((BYTE*)pvSrc); + break; + // 16 bit copy + case VT_I2: + case VT_UI2: + *((SHORT*)pvDst) = *((SHORT*)pvSrc); + break; + // 32 bit copy + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_ERROR: + *((ULONG*)pvDst) = *((ULONG*)pvSrc); + break; + // doubles (64 bit) + case VT_R8: + *((DOUBLE*)pvDst) = *((DOUBLE*)pvSrc); + break; + // currency + case VT_CY: + *((CY*)pvDst) = *((CY*)pvSrc); + break; + // date + case VT_DATE: + *((DATE*)pvDst) = *((DATE*)pvSrc); + break; + // boolean + case VT_BOOL: + *((VARIANT_BOOL*)pvDst) = *((VARIANT_BOOL*)pvSrc); + break; + // decimal + case VT_DECIMAL: + *((DECIMAL*)pvDst) = *((DECIMAL*)pvSrc); + break; + // TypeLib defined + case VT_USERDEFINED: + hr = m_pti->GetRefTypeInfo(vd->elemdescVar.tdesc.hreftype, &refInfo); + if (FAILED(hr)) return hr; + hr = GetRecordInfoFromTypeInfo2 (m_pti, &refrecInfo); + if (FAILED(hr)) return hr; + hr = refrecInfo->RecordCopy (pvSrc, pvDst); + if (FAILED(hr)) return hr; + break; + default: + break; + } + } + } + return S_OK; +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::GetGuid -- + * Retrieve GUID of struct. Can possibly be IID_NULL. + * + * Result: + * S_OK + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::GetGuid(GUID *pguid) +{ + *pguid = m_pta->guid; + return S_OK; +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::GetName -- + * Retrieve the name of the structure. + * + * Result: + * S_OK; + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::GetName(BSTR *pbstrName) +{ + *pbstrName = m_name.Copy(); + return (pbstrName!=NULL?S_OK:E_FAIL); +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::GetSize -- + * Retrieve the size, in bytes of the structure. + * + * Result: + * None. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::GetSize(ULONG *pcbSize) +{ + ATLASSERT (m_pta != NULL); + *pcbSize = m_pta->cbSizeInstance; + return S_OK; +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::GetTypeInfo -- + * Retrieve ITypeInfo for this structure. + * + * Result: + * S_OK iff all ok. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::GetTypeInfo(ITypeInfo **ppTypeInfo) +{ + ATLASSERT(m_pti != NULL); + return m_pti.CopyTo(ppTypeInfo); +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::GetField -- + * Retrieve the value of a given field within a structure of this type + * The value of the field is returned as a copy of the original. + * Result: + * + * Side Effects: + * + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::GetField(PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField) +{ + VARIANT refVar; + PVOID pvFieldData; + HRESULT hr; + + VariantInit (&refVar); + VariantClear(pvarField); + + hr = GetFieldNoCopy (pvData, szFieldName, &refVar, &pvFieldData); + if (FAILED(hr)) + return hr; + hr = VariantCopyInd(pvarField, &refVar); + return hr; +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::GetFieldNoCopy -- + * Retrieve a direct reference to the field's value using a VARDESC to identify the + * field. The caller must not free the returned variant. + * + * Result: + * S_OK iff ok. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::GetFieldNoCopy(PVOID pvData, VARDESC *pvd, VARIANT *pvarField, PVOID *ppvDataCArray) { + HRESULT hr; + hr = VariantClear (pvarField); + if (FAILED(hr)) return hr; + + // retrieve a pointer to the field data + PVOID pfield; + pfield = ( ((BYTE*)pvData) + pvd->oInst); + *ppvDataCArray = pfield; + + // now crack the field type ... + + // first some assertions ... + // not by-reference (COM Automation / Variant Declaration rules) + ATLASSERT ( (pvd->elemdescVar.tdesc.vt & VT_BYREF) == 0); + + if (pvd->elemdescVar.tdesc.vt == VT_USERDEFINED) { + // resolve the referenced type + CComPtr pRefInfo; + CComPtr pRefRecInfo; + hr = m_pti->GetRefTypeInfo (pvd->elemdescVar.tdesc.hreftype, &pRefInfo); + if (FAILED(hr)) + return hr; + hr = GetRecordInfoFromTypeInfo2 (pRefInfo, &pRefRecInfo); + if (FAILED(hr)) + return hr; + + // set the field reference and its record info + pvarField->pvRecord = pfield; + hr = pRefRecInfo.CopyTo(&(pvarField->pRecInfo)); + if (FAILED(hr)) + return hr; + pvarField->vt = VT_RECORD; + } else { + // in all other cases, we just set the pointer to the field member + pvarField->byref = pfield; + // the vartype of the resulting parameter will be a reference to the type of the field + pvarField->vt = (pvd->elemdescVar.tdesc.vt | VT_BYREF); + } + return S_OK; + +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::GetFieldNoCopy -- + * Retrieve the value of a field as a reference, given the name of the field. + * + * Result: + * S_OK iff ok. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::GetFieldNoCopy(PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray) +{ + HRESULT hr; + CVarDesc vd; + + hr = vd.Set(m_pti, szFieldName); + if (FAILED(hr)) return hr; + + hr = VariantClear (pvarField); + if (FAILED(hr)) return hr; + return GetFieldNoCopy (pvData, vd, pvarField, ppvDataCArray); +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::PutField -- + * Places a copy of the variant to the field, applying any type coercion + * as required. Rules for INVOKE_PROPERTYPUT are handled at a deeper + * level of call. + * + * Result: + * S_OK iff all ok. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::PutField(ULONG wFlags, PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField) +{ + CVarDesc vd; + HRESULT hr; + hr = vd.Set(m_pti, szFieldName); + if (FAILED(hr)) return hr; + + VARIANT varCopy; + VariantInit (&varCopy); + hr = VariantCopy (&varCopy, pvarField); + if (FAILED(hr)) return hr; + return PutFieldNoCopy (wFlags, pvData, vd, &varCopy); +} + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::PutFieldNoCopy -- + * Given the VARDESC for a field, this function places the value in + * pvarField to the field, without allocating any new resources. + * I'm not too sure about the INVOKE_PROPERTYPUT implementation + * which I've tried to follow from the MSDN documentation. As + * far as I can make out, the field must be of type VT_DISPATCH + * (or do I have to explicitly check for derivation from IDispatch?) + * The value is either of type VT_DISPATCH (in which case it's default + * property is used as the actual value), or any other valid variant + * sub-type. The actual value will be set to the default property of + * the field. + * + * Result: + * Standard COM result - S_OK iff all OK. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::PutFieldNoCopy(ULONG wFlags, PVOID pvData, VARDESC *pvd, VARIANT *pvarField) +{ + PVOID field = (BYTE*)pvData + pvd->oInst; + HRESULT hr; + + // perform the conversion ... + + if (wFlags == INVOKE_PROPERTYPUT) { + + // if the field isn't a dispatch object or is null then we fail + if (pvd->elemdescVar.tdesc.vt != VT_DISPATCH) + return E_FAIL; + + IDispatch * pdisp = *((IDispatch**)field); + if (pdisp == NULL) + return E_FAIL; + + CComVariant varResult; + DISPPARAMS dp; + DISPID dispidNamed = DISPID_PROPERTYPUT; + dp.cArgs = 1; + dp.cNamedArgs = 1; + dp.rgdispidNamedArgs = &dispidNamed; + dp.rgvarg = pvarField; + hr = pdisp->Invoke (DISPID_VALUE, IID_NULL, 0, DISPID_PROPERTYPUT, &dp, &varResult, NULL, NULL); + return hr; + } else { + // do a straight conversion + hr = VariantChangeType (pvarField, pvarField, NULL, pvd->elemdescVar.tdesc.vt); + if (FAILED(hr)) + return hr; + + // now perform a shallow copy + if (pvd->elemdescVar.tdesc.vt & VT_ARRAY != 0) { + *((SAFEARRAY**)field) = pvarField->parray; + } else { + switch (pvd->elemdescVar.tdesc.vt) { + // interfaces ... + case VT_UNKNOWN: + case VT_DISPATCH: + *((IUnknown**)field) = pvarField->punkVal; + break; + // string + case VT_BSTR: + *((BSTR*)field) = pvarField->bstrVal; + break; + // 8 bit copy + case VT_I1: + case VT_UI1: + *((BYTE*)field) = pvarField->bVal; + break; + // 16 bit copy + case VT_I2: + case VT_UI2: + *((SHORT*)field) = pvarField->iVal; + break; + // 32 bit copy + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_ERROR: + *((ULONG*)field) = pvarField->ulVal; + break; + // doubles (64 bit) + case VT_R8: + *((DOUBLE*)field) = pvarField->dblVal; + break; + // currency + case VT_CY: + *((CY*)field) = pvarField->cyVal; + break; + // date + case VT_DATE: + *((DATE*)field) = pvarField->date; + break; + // boolean + case VT_BOOL: + *((VARIANT_BOOL*)field) = pvarField->boolVal; + break; + // decimal + case VT_DECIMAL: + *((DECIMAL*)field) = pvarField->decVal; + break; + // TypeLib defined + case VT_USERDEFINED: + *((PVOID*)field) = pvarField->pvRecord; + break; + default: + break; + } + } + return S_OK; + } + +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::PutFieldNoCopy -- + * As the VARDESC variation above, but using the field name instead. + * + * Result: + * S_O iff all ok. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::PutFieldNoCopy(ULONG wFlags, PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField) +{ + CVarDesc vd; + HRESULT hr; + hr = vd.Set(m_pti, szFieldName); + if (FAILED(hr)) return hr; + return PutFieldNoCopy (wFlags, pvData, vd, pvarField); +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::GetFieldNames -- + * Retrieves an array of fields names. + * + * Result: + * S_OK iff all ok. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::GetFieldNames(ULONG *pcNames, BSTR *rgBstrNames) +{ + ULONG index = 0; + if (pcNames == NULL) + return E_INVALIDARG; + if (rgBstrNames == NULL) { + *pcNames = m_pta->cVars; + return S_OK; + } + + if (*pcNames > m_pta->cVars) + *pcNames = m_pta->cVars; + + try { + for (index = 0; index < *pcNames; index++) { + CVarDesc vd; + HRESULT hr; + hr = vd.Set (m_pti, index); + if (FAILED(hr)) + throw (hr); + + UINT dummy = 1; + hr = m_pti->GetNames (vd->memid, rgBstrNames+index, 1, &dummy); + if (FAILED(hr)) + throw(hr); + } + } catch (HRESULT hr) { + while (index > 0) + SysFreeString (rgBstrNames[--index]); + return hr; + } + return S_OK; +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::IsMatchingType -- + * Checks for equivalence of this record type and the one referenced by + * the only parameter. Because we can't guarantee the use of GUIDs + * I've settled for matching on the type and library name. + * + * Result: + * TRUE iff the record structures match. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +BOOL STDMETHODCALLTYPE CComRecordInfoImpl::IsMatchingType(IRecordInfo *pRecordInfo) +{ + BOOL result = FALSE; + CComBSTR bstrOtherName; + HRESULT hr; + + hr = pRecordInfo->GetName(&bstrOtherName); + if (FAILED(hr)) return FALSE; + + if (wcscmp(bstrOtherName, m_name) == 0) { + CComPtr pOtherInfo; + CComPtr pOurLib, pOtherLib; + UINT dummy; + TLIBATTR * pOurAttr = NULL, *pOtherAttr = NULL; + + hr = pRecordInfo->GetTypeInfo(&pOtherInfo); + if (FAILED (hr)) return FALSE; + + hr = pOtherInfo->GetContainingTypeLib(&pOtherLib, &dummy); + if (FAILED(hr)) return FALSE; + + hr = m_pti->GetContainingTypeLib(&pOurLib, &dummy); + if (FAILED(hr)) return FALSE; + + hr = pOurLib->GetLibAttr (&pOurAttr); + hr = pOtherLib->GetLibAttr (&pOtherAttr); + if (pOurAttr != NULL && pOtherAttr != NULL) + result = (pOurAttr->guid == pOtherAttr->guid); + if (pOurAttr != NULL) + pOtherLib->ReleaseTLibAttr (pOurAttr); + if (pOtherAttr != NULL) + pOtherLib->ReleaseTLibAttr (pOtherAttr); + } + return result; +} + + +/* + *------------------------------------------------------------------------- + * STDMETHODCALLTYPE CComRecordInfoImpl::RecordCreate -- + * Allocates (using the task memory allocator) a new record, and + * initialises it. + * + * Result: + * Pointer to the record structure iff successfull; else NULL. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +PVOID STDMETHODCALLTYPE CComRecordInfoImpl::RecordCreate( void) +{ + PVOID prec = CoTaskMemAlloc(m_pta->cbSizeInstance); + if (FAILED(RecordInit(prec))) { + CoTaskMemFree(prec); + prec = NULL; + } + return prec; +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::RecordCreateCopy -- + * Creates a copy of the passed record structure. + * + * Result: + * S_OK iff successfull. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::RecordCreateCopy(PVOID pvSource, PVOID *ppvDest) +{ + *ppvDest = RecordCreate(); + if (*ppvDest == NULL) + return E_FAIL; + return RecordCopy (pvSource, *ppvDest); +} + + +/* + *------------------------------------------------------------------------- + * CComRecordInfoImpl::RecordDestroy -- + * Clears the given record and releases the memory associated with it. + * + * Result: + * S_OK iff all OK. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +STDMETHODIMP CComRecordInfoImpl::RecordDestroy(PVOID pvRecord) +{ + HRESULT hr; + if (pvRecord) { + hr = RecordClear(pvRecord); + CoTaskMemFree(pvRecord); + } + return hr; +} + diff --git a/src/ComRecordInfoImpl.h b/src/ComRecordInfoImpl.h new file mode 100644 index 0000000..78a3798 --- /dev/null +++ b/src/ComRecordInfoImpl.h @@ -0,0 +1,61 @@ + + +/* + *------------------------------------------------------------------------- + * ComRecordInfoImpl.h + * Declares a IRecordInfo, that unlike the one shipped by MS, isn't + * reliant on the presence of a GUID for any structure. + * + * Copyright (C) 2000 Farzad Pezeshkpour + * Email: fuzz@sys.uea.ac.uk + * Date: 6th April 2000 + * + * How-To: 1) Add both this file and ComRecordInfoImpl.cpp to your project + * 2) Include this file where-ever you wish to access a structure + * using IRecordInfo. + * 3) Call GetRecordInfoFromTypeInfo2 instead of + * GetRecordInfoFromTypeInfo to retrieve an IRecordInfo. + * Licence: + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *------------------------------------------------------------------------- + */ + + + + +#if !defined(AFX_COMRECORDINFOIMPL_H__B3BDEDA0_FB84_11D3_9D8A_DFFCB467E034__INCLUDED_) +#define AFX_COMRECORDINFOIMPL_H__B3BDEDA0_FB84_11D3_9D8A_DFFCB467E034__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +/* + *------------------------------------------------------------------------- + * GetRecordInfoFromTypeInfo2 -- + * This is a replacement for GetRecordInfoFromTypeInfo. It returns an + * instance of the new IRecordInfo. + * + * Result: + * Standard COM result. S_OK iff all ok. + * + * Side Effects: + * Memory allocated for the new object implementing IRecordInfo. + *------------------------------------------------------------------------- + */ +HRESULT GetRecordInfoFromTypeInfo2 (ITypeInfo *pti, IRecordInfo **ppri); + +#endif // !defined(AFX_COMRECORDINFOIMPL_H__B3BDEDA0_FB84_11D3_9D8A_DFFCB467E034__INCLUDED_) diff --git a/src/EventBinding.cpp b/src/EventBinding.cpp index d8a12bd..af64f1a 100644 --- a/src/EventBinding.cpp +++ b/src/EventBinding.cpp @@ -537,7 +537,7 @@ int BindingProps::Eval (OptclObj *pObj, DISPPARAMS *pDispParams, LPVARIANT pVarR for (count = 0; count < pDispParams->cArgs; count++) { TObjPtr param; - if (!var2obj(m_pInterp, pDispParams->rgvarg[pDispParams->cArgs - count - 1], param, ppObjs+count)) + if (!var2obj(m_pInterp, pDispParams->rgvarg[pDispParams->cArgs - count - 1], NULL, param, ppObjs+count)) break; cmd.lappend(param, m_pInterp); } @@ -567,7 +567,7 @@ int BindingProps::Eval (OptclObj *pObj, DISPPARAMS *pDispParams, LPVARIANT pVarR if (result == TCL_ERROR) { - // do we have a exception storage + // do we have an exception storage if (pExcepInfo != NULL) { // fill it in diff --git a/src/FixedSplitter.tcl b/src/FixedSplitter.tcl new file mode 100644 index 0000000..6bed3ae --- /dev/null +++ b/src/FixedSplitter.tcl @@ -0,0 +1,24 @@ + +namespace eval FixedSplitter { + variable properties + proc _getState {w} { + uplevel { + variable properties + } + } + proc create {w args} { + _getState $w + + frame $w -width 200 -height 200 -relief sunken -bd 1 + + set properties($w-orient) horizontal + set properties($w-fixed) A + set properties($w-windowA) {} + set properties($w-windowB) {} + set properties($w-barwidth) 8 + set properties($w-fixedsize) 100 + + return $w + } + +} \ No newline at end of file diff --git a/src/ImageListBox.tcl b/src/ImageListBox.tcl new file mode 100644 index 0000000..3f3921f --- /dev/null +++ b/src/ImageListBox.tcl @@ -0,0 +1,335 @@ +proc ImageListBox {args} { + return [eval ImageListBox::create $args] +} + +namespace eval ImageListBox { + variable properties + array set properties {} + + proc _getState {w} { + uplevel {variable properties} + } + + proc create {w args} { + _getState $w + text $w -bd 0 -relief flat -width 30 -height 15 -state disabled -cursor arrow -wrap none + set properties($w-item) {} + set properties($w-nextId) 0 + set properties($w-active) 0 + set properties($w-anchor) 0 + set properties($w-selectmode) browse + set properties($w-uiselection) 0 + + $w tag configure ILB_Selection -background SystemHighlight -foreground SystemHighlightText + set font [$w cget -font] + $w tag configure ILB_Active -foreground red + + setBindings $w + rename ::$w [namespace current]::$w + proc ::$w {cmd args} "return \[eval [namespace current]::_dispatch $w \$cmd \$args\]" + + eval $w configure $args + return $w + } + + proc setBindings {w} { + foreach binding [bind Text] { + bind $w $binding "break;" + } + + foreach binding [bind Listbox] { + bind $w $binding "[bind Listbox $binding];break;" + } + + # special bindings + bind $w "[namespace current]::OnBeginUISelection $w; [bind Listbox ]; break;" + bind $w "[namespace current]::OnEndUISelection $w; [bind Listbox ]; break;" + return + } + + proc _dispatch {w cmd args} { + _getState $w + set cmds [info commands [namespace current]::$cmd*] + if {$cmds == {}} { + return [eval $w $cmd $args] + } else { + return [eval [lindex $cmds 0] $w $args] + } + } + + + + proc insert {w index args} { + _getState $w + + set bEnd [string match $index end] + if {!$bEnd} { + incr index + } else { + set index [expr int([$w index end])] + } + + $w config -state normal + foreach item $args { + $w image create $index.0 -align center -name _ILB_IMAGE_$properties($w-nextId) + $w insert $index.1 $item\n + $w tag add _ILB_TAG_$properties($w-nextId) $index.0 $index.end + + incr properties($w-nextId) + incr index + } + $w config -state disabled + } + + proc setimage {w index image} { + _getState $w + set index [index $w $index] + if {$index >= [index $w end]} { + set index [expr [index $w end] - 1] + } + set pos [expr $index + 1].0 + $w image configure $pos -image $image + } + + proc getimage {w index} { + _getState $w + set index [index $w $index] + set pos [expr $index + 1].0 + $w image cget $pos -image + } + + proc delete {w first {last {}} } { + _getState $w + + if {$last == {}} { + set last $first + } + set first [index $w $first] + set last [index $w $last] + + incr first + incr last 2 + $w config -state normal + $w delete $first.0 $last.0 + $w config -state disabled + } + + proc size {w} { + _getState $w + return [expr int([$w index end]) - 2] + } + + proc get {w first {last {}} } { + _getState $w + if {$last == {}} { + set last $first + } + set first [index $w $first] + set last [index $w $last] + if { [catch { + incr first + incr last + } ]} { + return {} + } + set result {} + while {$first <= $last} { + lappend result [$w get $first.0 $first.end] + incr first + } + return $result + } + + proc selection {w cmd args} { + _getState $w + switch -- $cmd { + clear { + eval _selectClear $w $args + } + includes { + eval _selectIncludes $w $args + } + set { + eval _selectSet $w $args + } + anchor { + eval _selectAnchor $w $args + } + default {error "unknown selection command: $cmd"} + } + } + + + proc _selectAnchor {w index} { + _getState $w + set properties($w-anchor) [index $w $index] + } + + proc _selectClear {w first {last {}} } { + + if {$last == {}} { + set last $first + } + set first [index $w $first] + set last [index $w $last] + + incr first; + incr last + + while {$first <= $last} { + $w tag remove ILB_Selection $first.0 [incr first].0 + } + } + + proc _selectSet {w args} { + _getState $w + $w tag remove ILB_Selection 1.0 end + + foreach index $args { + set index [index $w $index] + if {$index < [size $w]} { + $w tag add ILB_Selection [incr index].0 [incr index].0 + } + } + + if {!$properties($w-uiselection)} { + event generate $w <> + } + } + + + proc curselection {w} { + _getState $w + set index 0.0 + set result {} + while {[set range [$w tag nextrange ILB_Selection $index]] != {}} { + lappend result [expr int([lindex $range 0]) - 1] + set index [lindex $range 1] + } + return $result + } + + proc nearest {w y} { + set index [$w index @0,$y] + return [expr int($index) - 1] + } + + + proc see {w index} { + set index [index $w $index] + if {![string match $index end]} { + set index [expr $index + 1].0 + } + $w see $index + } + + proc index {w index} { + _getState $w + if {$index == {}} { + error "index can't be an empty string" + } + + switch -regexp -- $index { + {^(-)?[0-9]+$} {} + {^@(-)?[0-9]+,(-)?[0-9]+} { return [expr int([$w index $index]) - 1]} + active {return $properties($w-active)} + anchor {return $properties($w-anchor)} + end {return [size $w]} + default {error "unknown index value: $index"} + } + set size [size $w] + if {$index > $size} { + set index $size + } elseif {$index < 0} { + set index 0 + } + return $index + } + + proc activate {w index} { + _getState $w + set index [index $w $index] + set properties($w-active) $index + return + } + + proc bbox {w index} { + _getState $w + set index [index $w $index] + return [$w bbox $index.0] + } + + proc cget {w option} { + _getState $w + switch -- $option { + -selectmode {return $properties($w-selectmode)} + default {return [$w cget $option]} + } + } + + proc configure {w args} { + _getState $w + if {[llength $args]%2 != 0 && [llength $args] != 1} { + error "configure requires pairs" + } + set def {} + foreach {option value} $args { + switch -- $option { + -selectmode {return [_configSelectMode $w $value]} + default { + if {[llength $args] == 1} { + lappend def $option + } else { + lappend def $option $value + } + } + } + } + if {$def != {}} { + eval [namespace current]::$w configure $def + } + } + + + proc _configSelectMode {w value} { + _getState $w + if {$value == {}} { + return $properties($w-selectmode) + } else { + if {[regexp {^single|browse|multiple|extended$} $value]} { + error "invalid select mode: $value" + } + set properties($w-selectmode) $value + } + } + + proc OnBeginUISelection {w} { + _getState $w + set properties($w-uiselection) 1 + } + + proc OnEndUISelection {w} { + _getState $w + set properties($w-uiselection) 0 + event generate $w <> [namespace code "libs_onselect $w"] + + bind $lb {break;} + bind $lb [namespace code "libs_onkeypress $w %K; continue"] + + bind $lb [namespace code "libs_onenterkey $w"] + + + bind $lb "$closecmd; break" + bind $lb $browsecmd + bind $lb $savecmd + + bind $lb $closecmd + bind $lb $closecmd + bind $w [namespace code "libs_close %W $w"] + + focus $lb + $lb activate 0 + $lb selection set 0 + + wm geometry $w 600x300 + wm minsize $w 600 300 + optcl_utils::center_window $w + libs_update $w + + return $w + } + + proc libs_close {W w} { + if {[string match $w $W]} { + viewlib_clean + } + } + + proc libs_update {w} { + variable libs + set libs($w) {} + + set loaded {} + set notloaded {} + set loadednames {} + set notloadednames {} + $w.liblist.listbox delete 0 end + + foreach lib [array names ::typelib::typelibraries] { + mset {guid maj min} $lib + mset {name path} $typelib::typelibraries($lib) + if {[typelib::isloaded $guid $maj $min] != {}} { + lappend loaded [list $name $guid $maj $min $path] + } else { + lappend notloaded [list $name $guid $maj $min $path] + } + } + + foreach lib [lsort -dictionary -index 0 $loaded ] { + $w.liblist.listbox insert end [lindex $lib 0] + $w.liblist.listbox setimage end ::tlview::select_img + lappend libs($w) $lib + } + + foreach lib [lsort -dictionary -index 0 $notloaded ] { + $w.liblist.listbox insert end [lindex $lib 0] + $w.liblist.listbox setimage end ::tlview::noselect_img + lappend libs($w) $lib + } + $w.liblist.listbox selection set 0 + libs_onselect $w + } + + proc libs_onkeypress {w key} { + set lb $w.liblist.listbox + set key [string tolower $key] + if {![regexp {^[a-z]$} $key]} return + + set currentindex [$lb index active] + + set liblist [$lb get 0 end] + set searchlist [concat [lrange $liblist [expr $currentindex + 1] end] [lrange $liblist 0 $currentindex]] + + set nextindex [lsearch -regexp $searchlist ^($key|[string toupper $key]).*$] + + if {$nextindex>=0} { + if {$nextindex < [expr [llength $liblist] - $currentindex - 1]} { + set nextindex [expr $nextindex + $currentindex + 1] + } else { + set nextindex [expr $nextindex - ([llength $liblist] - $currentindex) + 1] + } + $lb selection clear 0 end + $lb activate $nextindex + $lb selection set $nextindex + $lb see active + } + } + + proc libs_onenterkey {w} { + set lb $w.liblist.listbox + set index [lindex [$lb curselection] 0] + libs_loader $w $index + } + + proc libs_loader {w index} { + variable libs + set lib [lindex $libs($w) $index] + mset {name guid maj min path} $lib + + set lb $w.liblist.listbox + set progname [typelib::isloaded $guid $maj $min] + if {$progname != {}} { + typelib::unload $progname + $lb setimage $index ::tlview::noselect_img + } else { + if {[catch {typelib::load $path} e]} { + tk_messageBox -title "Type Library Error" -message $e -icon error -parent $w -type ok + } else { + $lb setimage $index ::tlview::select_img + } + } + $lb activate $index + $lb selection clear 0 end + $lb selection set $index + } + + proc libs_ondblselect {w x y} { + set lb $w.liblist.listbox + set index [$lb index @$x,$y] + if {$index == {}} return + libs_loader $w $index + } + + proc libs_onselect {w} { + variable libs + set lb $w.liblist.listbox + set index [$lb curselection] + + set lib [lindex $libs($w) $index] + mset {name guid maj min path} $lib + $w.details_label config -text $name + $w.details.path config -text "Path: $path" + } + + + + proc libs_browse {w} { + variable libs + + set types { + {{Type Libraries} {.tlb .olb .dll}} + {{Executable Files} {.exe .dll}} + {{ActiveX Controls} {.ocx}} + {{All Files} {*}} + } + set fname [tk_getOpenFile -filetypes $types -parent $w -title "Add Type Library Reference"] + if {$fname != {}} { + try { + set progname [typelib::load $fname] + mset {guid maj min path fullname} [typelib::loadedlib_details $progname] + libs_update $w + set index [lsearch -exact $libs($w) [list $fullname $guid $maj $min $path]] + puts $index + if {$index >= 0} { + after 50 "$w.liblist.listbox selection set $index; $w.liblist.listbox see $index" + } + } catch {er} { + tk_messageBox -title "Error in loading library" -message $er -type ok -icon error + } + } + } + + + #------------------------------------------------------------------------------ + + + proc viewlib_clean {} { + variable properties + + history_clean + + set loadedlibs [typelib::loadedlibs] + + foreach item [array names properties] { + + if {[regexp -- - $item]} continue + if {![winfo exists $item]} { + array unset properties $item + array unset properties $item-* + continue + } + + if {[lsearch -exact $loadedlibs $properites($item-viewedlibrary)] < 0} { + # library is not loaded ... so revert to the current index of the history + history_current $item + } + } + + } + + proc viewlib_onenter {txt tag} { + $txt config -cursor hand2 + $txt tag configure $tag -underline 1 + } + + proc viewlib_onleave {txt tag} { + $txt config -cursor arrow + $txt tag configure $tag -underline 0 + } + + + proc viewlib_updatenav {w} { + set topbar $w.topbar + if {[history_back? $w]} { + $topbar.back config -state normal + } else { + $topbar.back config -state disabled + } + + if {[history_forward? $w]} { + $topbar.forward config -state normal + } else { + $topbar.forward config -state disabled + } + } + + + + proc viewlib_select {w lib {type {}} {element {}} {raise true}} { + variable properties + + history_lock $w + if {![string match $properties($w-viewedlibrary) $lib]} { + # try to find a window that is already viewing this library + foreach tlviewer [array names properties *-viewedlibrary] { + if {[string match $properties($tlviewer) $lib]} { + set w [lindex [split $tlviewer -] 0]; break + } + } + } + + set types_lb $w.sp1.sp2.types.listbox + set elements_lb $w.sp1.sp2.elements.listbox + + + # raise the window and instruct it to view the library + if {$raise} { + raise $w + } + + viewlib_showlibrary $w $lib + + + if {$type != {}} { + # now find the type + set index [lsearch -exact [$types_lb get 0 end] $type] + if {$index >= 0} { + $types_lb selection set $index + $types_lb see $index + } + } + + if {$type != {} && $element != {}} { + set index [lsearch -regexp [$elements_lb get 0 end] "^($element)( .+)?"] + if {$index >= 0} { + $elements_lb selection set $index + $elements_lb see $index + } + } + history_unlock $w + history_addwindowstate $w + } + + # browse to a specific library + proc viewlib_showlibrary {w lib} { + variable colors + variable properties + + set types_lb $w.sp1.sp2.types.listbox + set elements_lb $w.sp1.sp2.elements.listbox + set description $w.sp1.description.desc.t + + + $elements_lb delete 0 end + #$description config -state normal + $description delete 1.0 end + #$description config -state disabled + + # if the viewed library is being changed, redirect through the + # $w-library property change event handler + if {![string match $properties($w-viewedlibrary) $lib] && $lib != {}} { + set properties($w-viewedlibrary) $lib + return + } elseif {[string match $properties($w-library) $lib] || $lib == {}} { + if {$properties($w-type) != {}} { + set properties($w-type) {} + $types_lb selection clear 0 end + } + return + } + + set properties($w-library) $lib + set properties($w-type) {} + set properties($w-element) {} + + history_addwindowstate $w + + $types_lb delete 0 end + foreach tdesc [lsort [typelib::types $lib]] { + set typetype [lindex $tdesc 0] + set full [lindex $tdesc 1] + set type [lindex [split $full .] 1] + $types_lb insert end $type + + switch -- $typetype { + class {$types_lb setimage end ::tlview::class_img} + dispatch {$types_lb setimage end ::tlview::dispatch_img} + interface {$types_lb setimage end ::tlview::interface_img} + module {$types_lb setimage end ::tlview::module_img} + struct {$types_lb setimage end ::tlview::struct_img} + union {$types_lb setimage end ::tlview::union_img} + enum {$types_lb setimage end ::tlview::enum_img} + typedef {$types_lb setimage end ::tlview::typedef_img} + + } + + } + bind $types_lb <> [namespace code "viewlib_showelement_description_byindex $w $lib $type \[$elements_lb curselection\]"] + } + #$desc config -state disabled + } + + + + proc viewlib_showelement_description_byindex {w lib type elemindex} { + + + set elements_lb $w.sp1.sp2.elements.listbox + if {$elemindex == {}} { + viewlib_showelements $w $lib $type + return + } else { + set element [$elements_lb get $elemindex] + # because we tend to mark up default and source interfaces + # with appended symbols and names, we'll strip these off + set element [lindex [lindex $element 0] 0] + + # but in fact, any implemented type needs not an element + # description, but a jump to that types description + if {[regexp {^.+\..+$} $element]} { + set split [split $element .] + set newlib [lindex $split 0] + set newtype [lindex $split 1] + viewlib_select $w $newlib $newtype + } else { + viewlib_showelement_description $w $lib $type $element + } + } + } + + ### + # retrieves the description for an element + proc viewlib_showelement_description {w lib type elem} { + variable colors + variable properties + + set txt $w.sp1.description.desc.t + #$txt config -state normal + + $txt tag bind element <1> [namespace code "viewlib_select $lib.$type $elem"] + + # if we're not viewing this element already + if {$elem != {} && ![string match $properties($w-element) $elem]} { + $txt delete 1.0 end + set properties($w-element) $elem + history_addwindowstate $w + + set elementdesc [typelib::typeinfo $lib.$type $elem] + set elementkind [lindex $elementdesc 0] + + switch $elementkind { + property { + $txt insert end "property " + + set propertydesc [lindex $elementdesc 1] + # insert the flags + set flags [lindex $propertydesc 0] + if {[lsearch -exact $flags read] < 0} { + set flags {(write only)} + } elseif {[lsearch -exact $flags write] < 0} { + set flags {(read only)} + } elseif {$flags != {}} { + set flags {(read+write)} + } + $txt insert end "$flags\n" FlagDescription + + # the property type + viewlib_writetype $txt [lindex $propertydesc 1] + $txt insert end " " + + # the property name + $txt insert end "[lindex $propertydesc 2]" DescriptionLabel + + # now do the params + set params [lrange $propertydesc 3 end] + + foreach param $params { + $txt insert end "\n\t" + + if {[llength $param] == 3} { + $txt insert end "[lindex $param 0]\t" + set param [lrange $param 1 end] + } + viewlib_writetype $txt [lindex $param 0] + $txt insert end " [lrange $param 1 end]" + } + # the documentation for the property + set documentation [lindex $elementdesc 2] + if {$documentation != {}} { + $txt insert end "\n\n\"$documentation\"" MemberDocumentation + } + } + + method { + set methodesc [lindex $elementdesc 1] + $txt insert end "method\n" + + # the return type + viewlib_writetype $txt [lindex $methodesc 0] + $txt insert end " " + $txt insert end "[lindex $methodesc 1]" DescriptionLabel + set params [lrange $methodesc 2 end] + + foreach param $params { + $txt insert end "\n\t" + + if {[llength $param] == 3} { + $txt insert end "[lindex $param 0]\t" + set param [lrange $param 1 end] + } + viewlib_writetype $txt [lindex $param 0] + $txt insert end " [lrange $param 1 end]" + } + set documentation [lindex $elementdesc 2] + if {$documentation != {}} { + $txt insert end "\n\n\"$documentation\"" MemberDocumentation + } + } + } + } + #$txt config -state disabled + + } + + + proc viewlib_copy {w} { + variable properties + + set str $properties($w-viewedlibrary) + if {$properties($w-type) != {}} { + set str "$str.$properties($w-type)" + if {$properties($w-element)!={}} { + set str "$str $properties($w-element)" + } + } + clipboard clear + clipboard append -format STRING -- $str + } + + #### + # Creates a viewer for library + proc viewlib {{w {}} {lib {}}} { + variable colors + variable properties + + if {$w == {}} { + + # iterate over the current windows to find one that is viewing this library + foreach viewedlib [array names properties *-viewedlibrary] { + if {[string match $properties($viewedlib) $lib]} { + set w [lindex [split $viewedlib -] 0] + break; + } + } + } + + if {$w == {}} { + # make a unique name + set count 0 + + set w ._tlview_$count + while {[winfo exists $w]} { + set w ._tlview_[incr count] + } + } + + + if [winfo exists $w] { + raise $w + return $w + } + + toplevel $w -class TypeLibraryViewer -width 400 -height 300 + wm title $w "Type Library:" + + # top bar - search stuff + set topbar [frame $w.topbar] + pack $topbar -side top -fill x -pady 2 + pack [label $topbar.liblabel -text Library -underline 0 -width 6] -side left -anchor nw + + ::tlview::TlviewCombo::Create $topbar.libs {::typelib::loadedlibs} -textvariable [namespace current]::properties($w-viewedlibrary) + + $topbar.libs.e config -state disabled + pack $topbar.libs -side left -padx 3 + pack [button $topbar.back -image ::tlview::leftarrow_img -bd 1 -height 16 -width 16 -command [namespace code "history_back $w"]] -side left + pack [button $topbar.forward -image ::tlview::rightarrow_img -bd 1 -height 16 -width 16 -command [namespace code "history_forward $w"]] -side left + pack [button $topbar.copy -image ::tlview::copy_img -bd 1 -height 16 -width 16 -command [namespace code "viewlib_copy $w"]] -padx 3 -side left + pack [button $topbar.libselect -image ::tlview::libselect_img -bd 1 -height 16 -width 16 -command [namespace code "libs $w.selectlibs"]] -padx 0 -side left + + TlviewTooltip::Set $topbar.libs "Loaded Libraries" + TlviewTooltip::Set $topbar.back "Previous in History" + TlviewTooltip::Set $topbar.forward "Next in History" + TlviewTooltip::Set $topbar.copy "Copy" + TlviewTooltip::Set $topbar.libselect "Referenced Type Libraries" + + searchbox $w.searchbox + pack $w.searchbox -side top -fill x -pady 2 + + # splitters + set sp1 [Splitter $w.sp1 -orient horizontal -type fixB -position 160 -barwidth 5 -width 460 -height 380] + set sp2 [Splitter $w.sp1.sp2 -orient vertical -type fixA -position 200 -barwidth 5] + pack $sp1 -fill both -expand 1 -side bottom + $sp1 config -windowA $sp2 + + # description frame + set desc [frame $sp1.description] + $sp1 config -windowB $desc + pack [label $desc.label -text Description] -side top -anchor nw + pack [scrltxt $desc.desc] -side top -anchor nw -fill both -expand 1 + $desc.desc.t tag configure DescriptionLabel -foreground $colors(labelcolor) -font tlviewerbold + $desc.desc.t tag configure FlagDescription -font tlvieweritalic + $desc.desc.t tag configure MemberDocumentation -font tlvieweritalic -foreground $colors(labelcolor) + $desc.desc.t config -exportselection 1 -cursor xterm -insertontime 0 -selectforeground SystemHighlightText + bind $desc.desc.t {continue} + bind $desc.desc.t "break;" + + # types frame + set types [::tlview::TlviewScrolledListBox $sp2.types] + $types.listbox config -font {Arial 10 {}} + $sp2 config -windowA $types + $types.label config -text Types + + set elements [::tlview::TlviewScrolledListBox $sp2.elements] + $elements.listbox config -font {Arial 10 {}} + $sp2 config -windowB $elements + $elements.label config -text "Elements" + + if {$lib == {}} { + set lib [lindex [typelib::loadedlibs] 0] + } + + trace vdelete [namespace current]::properties($w-viewedlibrary) w [namespace code viewlib_libchanged] + set properties($w-viewedlibrary) {} + set properties($w-library) {} + set properties($w-type) {} + set properties($w-element) {} + trace variable [namespace current]::properties($w-viewedlibrary) w [namespace code viewlib_libchanged] + + bind $w [namespace code "viewlib_ondestroy %W"] + history_init $w + viewlib_showlibrary $w $lib + + return $w + } + + proc viewlib_ondestroy {w} { + variable properties + + if {[winfo toplevel $w] == $w} { + history_erase $w + array unset properties $w + array unset properties $w-* + } + } + + proc viewlib_libchanged {n1 n2 command} { + variable properties + set lib $properties($n2) + set w [lindex [split $n2 -] 0] + viewlib_showlibrary $w $lib + } + + proc viewtype {fullname {elem {}} {history 1}} { + set split [split $fullname .] + set lib [lindex $split 0] + set type [lindex $split 1] + + viewlib_select $lib $type $elem $history + } + + ### -- Search box code + + + proc searchbox {w} { + variable properties + + destroy $w + + frame $w + set splitter [winfo parent $w] + + frame $w.top + pack [label $w.top.searchlabel -text Search -underline 0 -width 6] -side left -anchor nw + ::tlview::TlviewCombo::Create $w.top.searchterm [namespace code "search_history_getlist $w"] + bind $w.top.searchterm.e [namespace code "searchbox_search $w"] + + button $w.top.search -image ::tlview::find_img -borderwidth 1 -command [namespace code "searchbox_search $w"] + button $w.top.showhide -image ::tlview::show_img -borderwidth 1 -command [namespace code "searchbox_showhide $w"] + pack $w.top.showhide $w.top.search -side right -padx 2 + pack $w.top.searchterm -side left -fill x -expand 1 -padx 3 + + TlviewTooltip::Set $w.top.showhide "Show/Hide Search Results" + TlviewTooltip::Set $w.top.searchterm "Search String" + TlviewTooltip::Set $w.top.search "Search for String" + + scrltxt $w.searchresults + $w.searchresults.t config -height 10 -tabs 5c -state disabled + + grid $w.top -sticky nsew + grid rowconfigure $w 1 -weight 1 + grid columnconfigure $w 0 -weight 1 + update + set properties($w-collapsed) [expr [winfo reqheight $w] + 2] + set properties($w-expanded) 200 + set properties($w-min) 90 + + #$splitter config -windowB $w -type fixB -position $properties($w-collapsed) -min $properties($w-collapsed) -max $properties($w-collapsed) + return $w + } + + proc searchbox_show {w} { + variable properties + if {[lsearch [grid slaves $w] $w.searchresults] >= 0} return + + set toplevel [winfo toplevel $w] + set windowheight [winfo height $toplevel] + set windowheight [expr $windowheight + [winfo reqheight $w.searchresults] + 5] + + grid $w.searchresults -row 1 -column 0 -sticky nsew -pady 5 + wm geometry $toplevel [winfo width $toplevel]x$windowheight + $w.top.showhide config -image ::tlview::hide_img -relief sunken + } + + proc searchbox_search {w} { + variable properties + set query [$w.top.searchterm.e get] + set lib $properties([winfo toplevel $w]-viewedlibrary) + + search_history_add $w $query + search $w $query + $w.top.searchterm.e selection clear + $w.top.searchterm.e selection range 0 end + } + + proc searchbox_hide {w} { + variable properties + + if {[lsearch [grid slaves $w] $w.searchresults] < 0} return + + set textheight [winfo reqheight $w.searchresults] + set toplevel [winfo toplevel $w] + set windowheight [expr [winfo height $toplevel] - $textheight - 5] + grid forget $w.searchresults + + wm geometry $toplevel [winfo width $toplevel]x$windowheight + $w.top.showhide config -image ::tlview::show_img -relief raised + } + + proc searchbox_showhide {w} { + if {[lsearch [grid slaves $w] $w.searchresults] < 0} { + searchbox_show $w + } else { + searchbox_hide $w + } + } + + proc search {w query} { + variable properties + + # ensure that the search window exists + + set w [winfo toplevel $w] + set lib $properties($w-viewedlibrary) + set searchbox $w.searchbox + set sr $searchbox.searchresults.t + + # set up the text box + $sr config -state normal + $sr delete 1.0 end + + searchbox_show $searchbox + + set query [join [list * $query *] {}] + + foreach desc [typelib::types $lib] { + set fulltype [lindex $desc 1] + set reflib [lindex [split $fulltype .] 0] + set reftype [lindex [split $fulltype .] 1] + + # perform search on the type name + if {[string match -nocase $query $reftype]} { + viewlib_writetype $sr $fulltype + $sr insert end "\n" + } + + # now iterate through its members + set typeinfo [typelib::typeinfo $fulltype] + foreach item [lindex $typeinfo 1] { + if {[string match -nocase $query $item]} { + viewlib_writetype $sr $fulltype.$item + $sr insert end "\n" + } + } + + foreach item [lindex $typeinfo 2] { + if {[string match -nocase $query $item]} { + viewlib_writetype $sr $fulltype.$item + $sr insert end "\n" + } + } + } + $sr config -state disabled + } + + proc viewtype {fulltype {element {}}} { + variable properties + set w {} + set split [split $fulltype .] + set lib [lindex $split 0] + set type [lindex $split 1] + + + set w [viewlib {} $lib] + update + viewlib_select $w $lib $type $element + } + + + proc class {obj} { + viewtype [optcl::class $obj] + } + + proc interface {obj} { + viewtype [optcl::interface $obj] + } +} diff --git a/src/Tooltip.tcl b/src/Tooltip.tcl new file mode 100644 index 0000000..8f1ebbc --- /dev/null +++ b/src/Tooltip.tcl @@ -0,0 +1,68 @@ + +# public interface +namespace eval TlviewTooltip { + proc Set {w text} { + variable properties + set properties($w-text) $text + bind $w [namespace code "Pending $w %X %Y"] + bind $w [namespace code "Hide"] + Hide + } + + proc Unset {w} { + bind $w {} + bind $w {} + Hide + } +} + + +# private stuff +namespace eval TlviewTooltip { + variable properties + set properties(window) .__tlview__tooltip + set properties(showbh) 1 + set properties(pending) {} + + + destroy $properties(window) + toplevel $properties(window) -bg SystemInfoText + label $properties(window).l -text "Tooltip" -bg SystemInfoBackground -fg SystemInfoText + pack $properties(window).l -padx 1 -pady 1 + + + wm overrideredirect $properties(window) 1 + wm withdraw $properties(window) + + + proc Pending {w x y} { + variable properties + Cancel + set properties(pending) [after 1000 [namespace code "Show $w $x $y"]] + } + + proc Cancel {} { + variable properties + if {$properties(pending) != {}} { + after cancel $properties(pending) + set properties(pending) {} + } + } + + proc Show {w x y} { + variable properties + + $properties(window).l configure -text $properties($w-text) + wm deiconify $properties(window) + incr x 8 + incr y 8 + wm transient $properties(window) $w + wm geometry $properties(window) +$x+$y + } + + proc Hide {} { + variable properties + Cancel + wm withdraw $properties(window) + } +} diff --git a/src/Utilities.tcl b/src/Utilities.tcl new file mode 100644 index 0000000..7fdcb39 --- /dev/null +++ b/src/Utilities.tcl @@ -0,0 +1,43 @@ + +namespace eval optcl_utils { +namespace export * +# nice and simple error catching +proc try {body catch errvarname catchblock} { + upvar $errvarname errvar + + if {![string match $catch catch]} { + error "invalid syntax - should be: try {body} catch {catchblock}" + } + if { [catch [list uplevel $body] errvar] } { + uplevel $catchblock + } else { + return $errvar + } +} + +proc throw {errmsg} { + uplevel [list error $errmsg] +} + + +proc center_window {w} { + set width [winfo reqwidth $w] + set height [winfo reqheight $w] + set swidth [winfo screenwidth $w] + set sheight [winfo screenheight $w] + + set x [expr ($swidth - $width) /2 ] + set y [expr ($sheight - $height) /2 ] + wm geometry $w +$x+$y +} + +# set multiple variables with the contents of the list +proc mset {vars values} { + foreach var $vars value $values { + if {$var == {}} {error "not enough variables for mset operation"} + upvar $var myvar + set myvar $value + } +} + +} \ No newline at end of file diff --git a/src/optcl.cpp b/src/optcl.cpp index d2fa2e1..941b689 100644 --- a/src/optcl.cpp +++ b/src/optcl.cpp @@ -38,7 +38,7 @@ HINSTANCE ghDll = NULL; CComModule _Module; CComPtr g_pmalloc; - +bool g_bTkInit = false; //---------------------------------------------------------------- // Function declarations @@ -394,7 +394,7 @@ TCL_CMDEF(OptclInvokeLibFunction) CHECKHR_TCL(hr, pInterp, TCL_ERROR); if (FAILED(hr)) return TCL_ERROR; - if (bOk = var2obj(pInterp, varResult, presult)) + if (bOk = var2obj(pInterp, varResult, NULL, presult)) Tcl_SetObjResult (pInterp, presult); VariantClear(&varResult); } @@ -623,6 +623,7 @@ int Optcl_Init (Tcl_Interp *pInterp) // initialise the Tk stubs - failure if (Tk_InitStubs (pInterp, "8.0", 0) == NULL) return TCL_ERROR; + g_bTkInit = true; } #else #error Wrong Tcl version for Stubs @@ -630,11 +631,14 @@ int Optcl_Init (Tcl_Interp *pInterp) #endif // USE_TCL_STUBS HRESULT hr; + Tcl_PkgProvide(pInterp, "optcl", "3.0"); + OleInitialize(NULL); hr = CoGetMalloc(1, &g_pmalloc); CHECKHR_TCL(hr, pInterp, TCL_ERROR); Tcl_CreateExitHandler (Optcl_Exit, NULL); + /* HRSRC hrsrc = FindResource (ghDll, MAKEINTRESOURCE(IDR_TYPELIB), _T("TCL_SCRIPT")); if (hrsrc == NULL) { Tcl_SetResult (pInterp, "failed to locate internal script", TCL_STATIC); @@ -652,6 +656,7 @@ int Optcl_Init (Tcl_Interp *pInterp) ASSERT (szscript != NULL); if (Tcl_GlobalEval (pInterp, szscript) == TCL_ERROR) return TCL_ERROR; + */ Tcl_CreateObjCommand (pInterp, "optcl::new", OptclNewCmd, NULL, NULL); Tcl_CreateObjCommand (pInterp, "optcl::lock", OptclLockCmd, NULL, NULL); diff --git a/src/optcl.dsp b/src/optcl.dsp index c4407bd..bc1dfd3 100644 --- a/src/optcl.dsp +++ b/src/optcl.dsp @@ -4,7 +4,7 @@ # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 -CFG=optcl - Win32 Debug_NoStubs +CFG=optcl - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE @@ -13,14 +13,13 @@ CFG=optcl - Win32 Debug_NoStubs !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 "optcl.mak" CFG="optcl - Win32 Debug_NoStubs" +!MESSAGE NMAKE /f "optcl.mak" CFG="optcl - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "optcl - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "optcl - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "optcl - Win32 Release_NoStubs" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "optcl - Win32 Debug_NoStubs" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "optcl - Win32 Release Static" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project @@ -41,11 +40,11 @@ RSC=rc.exe # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Intermediate_Dir "Release\Objects" # 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 "OPTCL_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /Ob2 /I "c:\progra~1\tcl\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPTCL_EXPORTS" /D "USE_TCL_STUBS" /D "USE_TK_STUBS" /FR /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O1 /Ob2 /I "c:\opt\tcl\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPTCL_EXPORTS" /D "USE_TCL_STUBS" /D "USE_TK_STUBS" /D TCL_THREADS=1 /D USE_THREAD_ALLOC=1 /D _REENTRANT=1 /D _THREAD_SAFE=1 /FR /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "NDEBUG" @@ -55,23 +54,24 @@ BSC32=bscmake.exe # 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 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib tclstub82.lib tkstub82.lib /nologo /dll /machine:I386 /out:"../install/optclstubs.dll" /libpath:"c:\progra~1\tcl\lib" +# ADD LINK32 tclstub84.lib tkstub84.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /dll /debug /machine:I386 /out:"Release/optcl30.dll" /libpath:"c:\opt\tcl\lib" +# SUBTRACT LINK32 /incremental:yes !ELSEIF "$(CFG)" == "optcl - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "optcl___Win32_Debug" -# PROP BASE Intermediate_Dir "optcl___Win32_Debug" +# 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 Intermediate_Dir "Debug\Objects" # 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 "OPTCL_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /Gi /GX /ZI /Od /I "c:\progra~1\tcl\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPTCL_EXPORTS" /D "USE_TCL_STUBS" /D "USE_TK_STUBS" /FR /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "c:\opt\tcl\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPTCL_EXPORTS" /D "USE_TCL_STUBS" /D "USE_TK_STUBS" /D "ATL_DEBUG_INTERFACES" /FR /YX"stdafx.h" /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "_DEBUG" @@ -81,24 +81,22 @@ BSC32=bscmake.exe # 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 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib tclstub84.lib tkstub84.lib /nologo /dll /debug /machine:I386 /out:"../install/optclstubs.dll" /pdbtype:sept /libpath:"c:\progra~1\tcl\lib" +# ADD LINK32 tclstub84.lib tkstub84.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /dll /debug /machine:I386 /out:"Debug/optcl30g.dll" /pdbtype:sept /libpath:"c:\opt\tcl\lib" -!ELSEIF "$(CFG)" == "optcl - Win32 Release_NoStubs" +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "optcl___Win32_Release_NoStubs" -# PROP BASE Intermediate_Dir "optcl___Win32_Release_NoStubs" -# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release\Static" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release_NoStubs" -# PROP Intermediate_Dir "Release_NoStubs" -# PROP Ignore_Export_Lib 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release\Static" # PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /Ob2 /I "c:\progra~1\tcl\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPTCL_EXPORTS" /D "USE_TCL_STUBS" /D "USE_TK_STUBS" /FR /Yu"stdafx.h" /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /Ob2 /I "c:\progra~1\tcl\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPTCL_EXPORTS" /FR /Yu"stdafx.h" /FD /c +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O1 /Ob2 /I "c:\opt\tcl\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_LIB" /D "OPTCL_EXPORTS" /D TCL_THREADS=1 /D USE_THREAD_ALLOC=1 /D _REENTRANT=1 /D _THREAD_SAFE=1 /FR /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "NDEBUG" @@ -107,35 +105,8 @@ 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 tclstub82.lib tkstub82.lib /nologo /dll /machine:I386 /libpath:"c:\progra~1\tcl\lib" -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib tcl80.lib tk80.lib /nologo /dll /machine:I386 /out:"../install/optcl80.dll" /libpath:"c:\progra~1\tcl\lib" - -!ELSEIF "$(CFG)" == "optcl - Win32 Debug_NoStubs" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "optcl___Win32_Debug_NoStubs" -# PROP BASE Intermediate_Dir "optcl___Win32_Debug_NoStubs" -# PROP BASE Ignore_Export_Lib 0 -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug_NoStubs" -# PROP Intermediate_Dir "Debug_NoStubs" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /Gi /GX /ZI /Od /I "c:\progra~1\tcl\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPTCL_EXPORTS" /D "USE_TCL_STUBS" /D "USE_TK_STUBS" /FR /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /Gi /GX /ZI /Od /I "c:\progra~1\tcl\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPTCL_EXPORTS" /FR /Yu"stdafx.h" /FD /GZ /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x809 /d "_DEBUG" -# ADD RSC /l 0x809 /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 tclstub82.lib tkstub82.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"c:\progra~1\tcl\lib" -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib tcl80.lib tk80.lib /nologo /dll /debug /machine:I386 /out:"../install/optcl80.dll" /pdbtype:sept /libpath:"c:\progra~1\tcl\lib" +# ADD BASE LINK32 /nologo /machine:IX86 +# ADD LINK32 /nologo /machine:IX86 /out:"Release\optcl30s.lib" /lib !ENDIF @@ -143,27 +114,30 @@ LINK32=link.exe # Name "optcl - Win32 Release" # Name "optcl - Win32 Debug" -# Name "optcl - Win32 Release_NoStubs" -# Name "optcl - Win32 Debug_NoStubs" -# Begin Group "Source" +# Name "optcl - Win32 Release Static" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File -# PROP Default_Filter "cpp" +SOURCE=.\ComRecordInfoImpl.cpp +# End Source File # Begin Source File SOURCE=.\Container.cpp !IF "$(CFG)" == "optcl - Win32 Release" -# ADD CPP /Yu"StdAfx.h" +# ADD CPP /YX"stdafx.h" !ELSEIF "$(CFG)" == "optcl - Win32 Debug" -!ELSEIF "$(CFG)" == "optcl - Win32 Release_NoStubs" +# ADD CPP /YX -# ADD BASE CPP /Yu"StdAfx.h" -# ADD CPP /Yu"StdAfx.h" +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" -!ELSEIF "$(CFG)" == "optcl - Win32 Debug_NoStubs" +# ADD BASE CPP /YX"stdafx.h" +# ADD CPP /YX"stdafx.h" !ENDIF @@ -174,21 +148,16 @@ SOURCE=.\DispParams.cpp !IF "$(CFG)" == "optcl - Win32 Release" -# ADD CPP /Yu"stdafx.h" +# ADD CPP /YX"stdafx.h" !ELSEIF "$(CFG)" == "optcl - Win32 Debug" -# ADD CPP /Yu"StdAfx.h" - -!ELSEIF "$(CFG)" == "optcl - Win32 Release_NoStubs" - -# ADD BASE CPP /Yu"stdafx.h" -# ADD CPP /Yu"stdafx.h" +# ADD CPP /YX -!ELSEIF "$(CFG)" == "optcl - Win32 Debug_NoStubs" +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" -# ADD BASE CPP /Yu"StdAfx.h" -# ADD CPP /Yu"StdAfx.h" +# ADD BASE CPP /YX"stdafx.h" +# ADD CPP /YX"stdafx.h" !ENDIF @@ -199,16 +168,16 @@ SOURCE=.\EventBinding.cpp !IF "$(CFG)" == "optcl - Win32 Release" -# ADD CPP /Yu"stdafx.h" +# ADD CPP /YX"stdafx.h" !ELSEIF "$(CFG)" == "optcl - Win32 Debug" -!ELSEIF "$(CFG)" == "optcl - Win32 Release_NoStubs" +# ADD CPP /YX -# ADD BASE CPP /Yu"stdafx.h" -# ADD CPP /Yu"stdafx.h" +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" -!ELSEIF "$(CFG)" == "optcl - Win32 Debug_NoStubs" +# ADD BASE CPP /YX"stdafx.h" +# ADD CPP /YX"stdafx.h" !ENDIF @@ -216,7 +185,22 @@ SOURCE=.\EventBinding.cpp # Begin Source File SOURCE=.\initonce.cpp -# SUBTRACT CPP /YX /Yc /Yu + +!IF "$(CFG)" == "optcl - Win32 Release" + +# ADD CPP /YX"stdafx.h" + +!ELSEIF "$(CFG)" == "optcl - Win32 Debug" + +# SUBTRACT CPP /YX + +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" + +# ADD BASE CPP /YX"stdafx.h" +# ADD CPP /YX"stdafx.h" + +!ENDIF + # End Source File # Begin Source File @@ -224,21 +208,16 @@ SOURCE=.\ObjMap.cpp !IF "$(CFG)" == "optcl - Win32 Release" -# ADD CPP /Yu"stdafx.h" +# ADD CPP /YX"stdafx.h" !ELSEIF "$(CFG)" == "optcl - Win32 Debug" -# ADD CPP /Yu"StdAfx.h" - -!ELSEIF "$(CFG)" == "optcl - Win32 Release_NoStubs" +# ADD CPP /YX -# ADD BASE CPP /Yu"stdafx.h" -# ADD CPP /Yu"stdafx.h" +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" -!ELSEIF "$(CFG)" == "optcl - Win32 Debug_NoStubs" - -# ADD BASE CPP /Yu"StdAfx.h" -# ADD CPP /Yu"StdAfx.h" +# ADD BASE CPP /YX"stdafx.h" +# ADD CPP /YX"stdafx.h" !ENDIF @@ -249,21 +228,16 @@ SOURCE=.\optcl.cpp !IF "$(CFG)" == "optcl - Win32 Release" -# ADD CPP /Yu"stdafx.h" +# ADD CPP /YX"stdafx.h" !ELSEIF "$(CFG)" == "optcl - Win32 Debug" -# ADD CPP /Yu"StdAfx.h" - -!ELSEIF "$(CFG)" == "optcl - Win32 Release_NoStubs" +# ADD CPP /YX -# ADD BASE CPP /Yu"stdafx.h" -# ADD CPP /Yu"stdafx.h" +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" -!ELSEIF "$(CFG)" == "optcl - Win32 Debug_NoStubs" - -# ADD BASE CPP /Yu"StdAfx.h" -# ADD CPP /Yu"StdAfx.h" +# ADD BASE CPP /YX"stdafx.h" +# ADD CPP /YX"stdafx.h" !ENDIF @@ -271,6 +245,22 @@ SOURCE=.\optcl.cpp # Begin Source File SOURCE=.\OptclBindPtr.cpp + +!IF "$(CFG)" == "optcl - Win32 Release" + +# ADD CPP /YX"stdafx.h" + +!ELSEIF "$(CFG)" == "optcl - Win32 Debug" + +# ADD CPP /YX + +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" + +# ADD BASE CPP /YX"stdafx.h" +# ADD CPP /YX"stdafx.h" + +!ENDIF + # End Source File # Begin Source File @@ -278,48 +268,97 @@ SOURCE=.\OptclObj.cpp !IF "$(CFG)" == "optcl - Win32 Release" -# ADD CPP /Yu"stdafx.h" +# ADD CPP /YX"stdafx.h" !ELSEIF "$(CFG)" == "optcl - Win32 Debug" -# ADD CPP /Yu"StdAfx.h" +# ADD CPP /YX -!ELSEIF "$(CFG)" == "optcl - Win32 Release_NoStubs" +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" -# ADD BASE CPP /Yu"stdafx.h" -# ADD CPP /Yu"stdafx.h" +# ADD BASE CPP /YX"stdafx.h" +# ADD CPP /YX"stdafx.h" -!ELSEIF "$(CFG)" == "optcl - Win32 Debug_NoStubs" +!ENDIF -# ADD BASE CPP /Yu"StdAfx.h" -# ADD CPP /Yu"StdAfx.h" +# End Source File +# Begin Source File + +SOURCE=.\OptclTypeAttr.cpp + +!IF "$(CFG)" == "optcl - Win32 Release" + +# ADD CPP /YX"stdafx.h" + +!ELSEIF "$(CFG)" == "optcl - Win32 Debug" + +# ADD CPP /YX + +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" + +# ADD BASE CPP /YX"stdafx.h" +# ADD CPP /YX"stdafx.h" !ENDIF # End Source File # Begin Source File -SOURCE=.\OptclTypeAttr.cpp +SOURCE=.\resource.rc # End Source File # Begin Source File SOURCE=.\StdAfx.cpp -# ADD CPP /Yc"StdAfx.h" +# ADD CPP /Yc"stdafx.h" # End Source File # Begin Source File SOURCE=.\typelib.cpp -# ADD CPP /Yu"StdAfx.h" + +!IF "$(CFG)" == "optcl - Win32 Release" + +# ADD CPP /YX"stdafx.h" + +!ELSEIF "$(CFG)" == "optcl - Win32 Debug" + +# ADD CPP /YX + +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" + +# ADD BASE CPP /YX"stdafx.h" +# ADD CPP /YX"stdafx.h" + +!ENDIF + # End Source File # Begin Source File SOURCE=.\utility.cpp -# ADD CPP /Yu"StdAfx.h" + +!IF "$(CFG)" == "optcl - Win32 Release" + +# ADD CPP /YX"stdafx.h" + +!ELSEIF "$(CFG)" == "optcl - Win32 Debug" + +# ADD CPP /YX + +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" + +# ADD BASE CPP /YX"stdafx.h" +# ADD CPP /YX"stdafx.h" + +!ENDIF + # End Source File # End Group -# Begin Group "Header" +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File -# PROP Default_Filter "h" +SOURCE=.\ComRecordInfoImpl.h +# End Source File # Begin Source File SOURCE=.\Container.h @@ -373,42 +412,272 @@ SOURCE=.\typelib.h SOURCE=.\utility.h # End Source File # End Group -# Begin Group "Resource" +# Begin Group "Resource Files" -# PROP Default_Filter "" +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File -SOURCE=.\resource.rc +SOURCE=.\ImageListBox.tcl + +!IF "$(CFG)" == "optcl - Win32 Release" + +# Begin Custom Build +InputPath=.\ImageListBox.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Debug" + +# Begin Custom Build +InputPath=.\ImageListBox.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" + +# Begin Custom Build +InputPath=.\ImageListBox.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\optcl.tcl + +!IF "$(CFG)" == "optcl - Win32 Release" + +# Begin Custom Build +InputPath=.\optcl.tcl + +"c:\progra~1\tcl\lib\optcl\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Debug" + +# Begin Custom Build +InputPath=.\optcl.tcl + +"c:\progra~1\tcl\lib\optcl\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" + +# Begin Custom Build +InputPath=.\optcl.tcl + +"c:\progra~1\tcl\lib\optcl\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\$(InputPath) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Splitter.tcl + +!IF "$(CFG)" == "optcl - Win32 Release" + +# Begin Custom Build +InputPath=.\Splitter.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Debug" + +# Begin Custom Build +InputPath=.\Splitter.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" + +# Begin Custom Build +InputPath=.\Splitter.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\TLView.tcl + +!IF "$(CFG)" == "optcl - Win32 Release" + +# Begin Custom Build +InputPath=.\TLView.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Debug" + +# Begin Custom Build +InputPath=.\TLView.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" + +# Begin Custom Build +InputPath=.\TLView.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Tooltip.tcl + +!IF "$(CFG)" == "optcl - Win32 Release" + +# Begin Custom Build +InputPath=.\Tooltip.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Debug" + +# Begin Custom Build +InputPath=.\Tooltip.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" + +# Begin Custom Build +InputPath=.\Tooltip.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ENDIF + # End Source File # Begin Source File SOURCE=.\typelib.tcl + +!IF "$(CFG)" == "optcl - Win32 Release" + +# Begin Custom Build +InputPath=.\typelib.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Debug" + +# Begin Custom Build +InputPath=.\typelib.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" + +# Begin Custom Build +InputPath=.\typelib.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build + +!ENDIF + # End Source File -# End Group # Begin Source File -SOURCE=.\test.tcl +SOURCE=.\Utilities.tcl !IF "$(CFG)" == "optcl - Win32 Release" -# PROP Exclude_From_Build 1 +# Begin Custom Build +InputPath=.\Utilities.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build !ELSEIF "$(CFG)" == "optcl - Win32 Debug" -# PROP Exclude_From_Build 1 +# Begin Custom Build +InputPath=.\Utilities.tcl + +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) -!ELSEIF "$(CFG)" == "optcl - Win32 Release_NoStubs" +# End Custom Build -# PROP BASE Exclude_From_Build 1 -# PROP Exclude_From_Build 1 +!ELSEIF "$(CFG)" == "optcl - Win32 Release Static" -!ELSEIF "$(CFG)" == "optcl - Win32 Debug_NoStubs" +# Begin Custom Build +InputPath=.\Utilities.tcl -# PROP BASE Exclude_From_Build 1 -# PROP Exclude_From_Build 1 +"c:\progra~1\tcl\lib\optcl\scripts\$(InputPath)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy $(InputPath) c:\progra~1\tcl\lib\optcl\scripts\$(InputPath) + +# End Custom Build !ENDIF +# End Source File +# End Group +# Begin Source File + +SOURCE=.\test.tcl # End Source File # End Target # End Project diff --git a/src/optcl.dsw b/src/optcl.dsw new file mode 100644 index 0000000..1b00bbc --- /dev/null +++ b/src/optcl.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "optcl"=.\optcl.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/src/optcl.h b/src/optcl.h index 7e21c66..e50e25e 100644 --- a/src/optcl.h +++ b/src/optcl.h @@ -41,5 +41,5 @@ int TypeLib_Init (Tcl_Interp *pInterp); extern CComPtr g_pmalloc; - +extern bool g_bTkInit; #endif// _OPTCL_H_B229D2A0_616A_11d4_8004_0040055861F2 \ No newline at end of file diff --git a/src/optcl.tcl b/src/optcl.tcl new file mode 100644 index 0000000..c92fd71 --- /dev/null +++ b/src/optcl.tcl @@ -0,0 +1,10 @@ +package provide optcl 3.0 +set env(OPTCL_LIBRARY) [file dirname [info script]] +source [file join $env(OPTCL_LIBRARY) scripts Utilities.tcl] +source [file join $env(OPTCL_LIBRARY) scripts Splitter.tcl] +source [file join $env(OPTCL_LIBRARY) scripts TypeLib.tcl] +source [file join $env(OPTCL_LIBRARY) scripts ImageListBox.tcl] +source [file join $env(OPTCL_LIBRARY) scripts Tooltip.tcl] +if {[info commands tk] != {}} {source [file join $env(OPTCL_LIBRARY) scripts TLView.tcl]} +load [file join $env(OPTCL_LIBRARY) bin optcl.dll] +typelib::updateLibs \ No newline at end of file diff --git a/src/resource.aps b/src/resource.aps deleted file mode 100644 index 28c86bb..0000000 Binary files a/src/resource.aps and /dev/null differ diff --git a/src/resource.h b/src/resource.h index d253af9..74f1227 100644 --- a/src/resource.h +++ b/src/resource.h @@ -9,7 +9,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_RESOURCE_VALUE 104 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/src/resource.rc b/src/resource.rc index 52e366d..54e4d81 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -21,13 +21,6 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK #pragma code_page(1252) #endif //_WIN32 -///////////////////////////////////////////////////////////////////////////// -// -// TCL_SCRIPT -// - -IDR_TYPELIB TCL_SCRIPT DISCARDABLE "typelib.tcl" - #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // @@ -80,15 +73,15 @@ BEGIN VALUE "Comments", "Requires Tcl/Tk major version 8, minor version >= 1\0" VALUE "CompanyName", "University of East Anglia\0" VALUE "FileDescription", "A Tcl extension for manipulating COM objects.\0" - VALUE "FileVersion", "3,0,0,2\0" + VALUE "FileVersion", "3,0,1,0\0" VALUE "InternalName", "optcl\0" VALUE "LegalCopyright", "Copyright © 1999\0" VALUE "LegalTrademarks", "-\0" VALUE "OriginalFilename", "optcl.dll\0" VALUE "PrivateBuild", "-\0" VALUE "ProductName", "OpTcl\0" - VALUE "ProductVersion", "3,0,0,2\0" - VALUE "SpecialBuild", "-\0" + VALUE "ProductVersion", "3,0,1,0\0" + VALUE "SpecialBuild", "threaded\0" END END BLOCK "VarFileInfo" diff --git a/src/test.tcl b/src/test.tcl index 86b8d60..278d3b3 100644 --- a/src/test.tcl +++ b/src/test.tcl @@ -1,100 +1,8 @@ console show -load optcl +package require optcl - - -proc ie_test {} { - global ie - set ie [optcl::new -window .ie {{8856F961-340A-11D0-A96B-00C04FD705A2}}] - pack .ie - $ie navigate www.wired.com -} - -proc vrml_test {} { - global vrml - set vrml [optcl::new -window .vrml {{4B6E3013-6E45-11D0-9309-0020AFE05CC8}}] - pack .vrml -} - -proc tree_test {} { - global tv - set tv [optcl::new -window .tv {{C74190B6-8589-11D1-B16A-00C0F0283628}}] - pack .tv - set n1 [$tv -with nodes add] - $n1 : text "Node 1" key "1 Node" - optcl::unlock $n1 - set n2 [$tv -with nodes add "1 Node" 4 "2 Node" "Node 2"] - $n2 : text "Node 2.5" - optcl::unlock $n2 -} - -proc dp_test {} { - global dp - destroy .date - set dp [optcl::new -window .date MSComCtl2.DTPicker] - .date config -width 100 -height 20 - pack .date - tlview::viewtype [optcl::class $dp] -} - -proc cal_test {} { - global cal - destroy .cal - set cal [optcl::new -window .cal MSCAL.Calendar] - pack .cal -} - - -proc pb_test {} { - global pb mousedown - - proc PBMouseDown {obj args} { - global mousedown - set mousedown $obj - } - - proc PBMouseUp {args} { - global mousedown - set mousedown {} - } - - proc PBMouseMove {obj button shift x y} { - global mousedown - if {$mousedown == {}} return - if {[string compare $mousedown $obj]==0} { - $obj : value $x - } - } - destroy .pb - set pb [optcl::new -window .pb MSComctlLib.ProgCtrl] - pack .pb - .pb config -width 100 -height 10 - optcl::bind $pb MouseDown PBMouseDown - optcl::bind $pb MouseUp PBMouseUp - optcl::bind $pb MouseMove PBMouseMove -} - - - - -proc word_test {} { - global word - - set word [optcl::new word.application] - $word : visible 1 -} - - -proc tl_test {} { - typelib::load {Microsoft Shell Controls And Automation (Ver 1.0)} - tlview::refview .r - tlview::loadedlibs .l -} - - - -proc cosmo_test {} { - global co - set co [optcl::new -window .co SGI.CosmoPlayer.2] - pack .co -} +set xl [optcl::new Excel.Application] +$xl : visible 1 +$xl -with workbooks add +$xl -with workbooks.item(1).worksheets.item(1).range(a1,d4) : value 16 +set r [$xl -with workbooks.item(1).worksheets.item(1) range a1 d4] diff --git a/src/typelib.cpp b/src/typelib.cpp index 4e2ec52..916ea4b 100644 --- a/src/typelib.cpp +++ b/src/typelib.cpp @@ -32,7 +32,8 @@ #include "typelib.h" #include "objmap.h" #include "optclbindptr.h" - +#include "optcltypeattr.h" +#include //---------------------------------------------------------------- // \/\/\/\/\/\ Declarations /\/\/\/\/\/\/ @@ -63,7 +64,9 @@ TCL_CMDEF(TypeLib_UnloadLib); TCL_CMDEF(TypeLib_IsLibLoaded); TCL_CMDEF(TypeLib_TypesInLib); TCL_CMDEF(TypeLib_TypeInfo); - +TCL_CMDEF(TypeLib_GetRegLibPath); +TCL_CMDEF(TypeLib_GetLoadedLibPath); +TCL_CMDEF(TypeLib_GetDetails); //// TEST CODE //// TCL_CMDEF(TypeLib_ResolveConstantTest); @@ -102,65 +105,35 @@ void TypeLibsTbl::DeleteAll () deltbl(); } - -ITypeLib * TypeLibsTbl::LoadLib (Tcl_Interp *pInterp, const char *fullname) +/* + *------------------------------------------------------------------------- + * TypeLibsTbl::LoadLib -- + * Load a Type Library by it's pathname. + * + * Result: + * Pointer to the TypeLib object iff successful. + * Side Effects: + * Library is added to the cache + *------------------------------------------------------------------------- + */ +TypeLib * TypeLibsTbl::LoadLib (Tcl_Interp *pInterp, const char *pathname) { USES_CONVERSION; CComPtr pLib; - CComPtr pComp; - - TObjPtr cmd, // used to build up a command string - result, // attaches to the result of the above commands' execution - progname; // the programmatic name of the library - GUID guid; - int maj, min; - HRESULT hr; - - Tcl_HashEntry *pEntry = NULL; - TypeLib *ptl; - - if (m_loadedlibs.find(fullname, &pEntry) != NULL) { - ASSERT (pEntry != NULL); - ptl = (TypeLib *)Tcl_GetHashValue (pEntry); - ASSERT (ptl != NULL); - Tcl_SetResult (pInterp, Tcl_GetHashKey (&m_tbl, pEntry), TCL_VOLATILE); - pLib = ptl->m_ptl; - ASSERT (pLib != NULL); - return pLib; - } - - + try { - // get the guid, max and min version numbers - cmd.create(); - cmd = "typelib::libdetail"; - cmd.lappend (fullname); - if (Tcl_GlobalEvalObj (pInterp, cmd) == TCL_ERROR) return NULL; - result.attach(Tcl_GetObjResult(pInterp), false); - if (result.llength() != 3) - throw ("expected three elements in the library description"); - - maj = result.lindex (1); - min = result.lindex (2); - hr = CLSIDFromString (A2OLE(result.lindex(0)), &guid); - if (FAILED(hr)) - throw ("failed to convert identifier"); - - // load the library - hr = LoadRegTypeLib (guid, maj, min, LOCALE_SYSTEM_DEFAULT, &pLib); + CComPtr pComp; + TObjPtr progname, fullname; + Tcl_HashEntry *pEntry = NULL; + HRESULT hr; + + hr = LoadTypeLibEx(A2OLE(pathname), REGKIND_NONE, &pLib); CHECKHR(hr); + ASSERT(pLib != NULL); if (pLib == NULL) throw ("failed to bind to a type library"); - // get the programmatic name of the library - TypeLib_GetName (pLib, NULL, progname); - - hr = pLib->GetTypeComp(&pComp); - if (FAILED(hr)) - throw ("failed to get the compiler interface for library"); - - Cache (progname, fullname, pLib, pComp); - Tcl_SetResult (pInterp, (char*)(const char*)progname, TCL_VOLATILE); + return Cache (pInterp, pLib, pathname); } catch (char *error) { @@ -170,7 +143,7 @@ ITypeLib * TypeLibsTbl::LoadLib (Tcl_Interp *pInterp, const char *fullname) Tcl_SetResult (pInterp, (char*)HRESULT2Str(hr), TCL_DYNAMIC); } - return pLib; + return NULL; } @@ -188,32 +161,107 @@ ITypeLib * TypeLibsTbl::LoadLib (Tcl_Interp *pInterp, const char *fullname) * None. *------------------------------------------------------------------------- */ -TypeLib* TypeLibsTbl::Cache (const char *szname, const char *szfullname, ITypeLib *ptl, ITypeComp *ptc) +TypeLib* TypeLibsTbl::Cache (Tcl_Interp *pInterp, ITypeLib *ptl, const char * path /* = NULL */) { - ASSERT(szname != NULL && szfullname != NULL); - ASSERT (ptl != NULL && ptc != NULL); + TLIBATTR * ptlattr = NULL; + CComPtr ptc; TypeLib *pLib = NULL; Tcl_HashEntry *pEntry = NULL; - pLib = new TypeLib (ptl, ptc); - pEntry = set(szname, pLib); - ASSERT (pEntry != NULL); - m_loadedlibs.set (szfullname, pEntry); + if (FAILED(ptl->GetLibAttr(&ptlattr))) { + if (pInterp) + Tcl_SetResult (pInterp, "couldn't retrieve type library attributes", TCL_STATIC); + return NULL; + } + + ASSERT(ptlattr != NULL); + + TypeLibUniqueID uid (ptlattr->guid, ptlattr->wMajorVerNum, ptlattr->wMinorVerNum); + + + ptl->ReleaseTLibAttr(ptlattr); + + // search for this guid + if (m_loadedlibs.find(&uid, &pEntry) != NULL) { + ASSERT (pEntry != NULL); + pLib = (TypeLib *)Tcl_GetHashValue (pEntry); + return pLib; + } + + // now generate the names, and do a search on the programmatic name + TObjPtr progname, fullname; + GenerateNames(progname, fullname, ptl); + + if (g_libs.find((char*)progname, &pLib) != NULL) { + if (pInterp) + Tcl_SetResult (pInterp, "library already loaded with the same programmatic name", TCL_STATIC); + return NULL; + } + + if (FAILED(ptl->GetTypeComp(&ptc))) { + if (pInterp) + Tcl_SetResult (pInterp, "failed to retrieve the ITypeComp interface", TCL_STATIC); + return NULL; + } + + pLib = new TypeLib (); + if (FAILED(pLib->Init(ptl, ptc, progname, fullname, path))) { + delete pLib; + pLib = NULL; + } else { + pEntry = set(progname, pLib); + ASSERT (pEntry != NULL); + m_loadedlibs.set (&uid, pEntry); + } + return pLib; } -bool TypeLibsTbl::IsLibLoaded (const char *fullname) +TypeLib * TypeLibsTbl::TypeLibFromUID (const GUID &guid, WORD maj, WORD min) { - ASSERT (fullname != NULL); - return (m_loadedlibs.find (fullname) != NULL); + TypeLibUniqueID uid(guid, maj, min); + TypeLib *plib = NULL; + Tcl_HashEntry *pEntry = NULL; + m_loadedlibs.find(&uid, &pEntry); + if (pEntry) + plib = (TypeLib*)Tcl_GetHashValue (pEntry); + return plib; } + +char * TypeLibsTbl::GetFullName (char * szProgName) +{ + ASSERT (szProgName != NULL); + TypeLib * pLib = NULL; + char * result = NULL; + if (find(szProgName, &pLib) != end()) { + ASSERT (pLib != NULL); + result = pLib->m_fullname; + } + return result; +} + + + +GUID * TypeLibsTbl::GetGUID (char * szProgName) +{ + ASSERT (szProgName != NULL); + TypeLib * pLib = NULL; + GUID * result = NULL; + if (find(szProgName, &pLib) != end()) { + ASSERT (pLib != NULL && pLib->m_libattr != NULL); + result = &(pLib->m_libattr->guid); + } + return result; +} + + /* *------------------------------------------------------------------------- * TypeLibsTbl::UnloadLib -- - * Given the fullname of a library, the routine unloads it, if it is + * Given the programmatic name of a library, the routine unloads it, if it is * loaded. * * Result: @@ -223,22 +271,73 @@ bool TypeLibsTbl::IsLibLoaded (const char *fullname) * None. *------------------------------------------------------------------------- */ -void TypeLibsTbl::UnloadLib (Tcl_Interp *pInterp, const char *fullname) +void TypeLibsTbl::UnloadLib (Tcl_Interp *pInterp, const char *szprogname) { Tcl_HashEntry *pEntry = NULL; TypeLib *ptl = NULL; + pEntry = g_libs.find(szprogname, &ptl); - if (!m_loadedlibs.find (fullname, &pEntry)) + if (pEntry == NULL) return; - ASSERT (pEntry != NULL); - ptl = (TypeLib*)Tcl_GetHashValue (pEntry); - ASSERT (ptl != NULL); + ASSERT (ptl != NULL && ptl->m_ptl != NULL); + + TObjPtr progname, fullname; + HRESULT hr = GenerateNames(progname, fullname, ptl->m_ptl); + + if (FAILED(hr)) { + Tcl_SetResult (pInterp, HRESULT2Str(hr), TCL_DYNAMIC); + return; + } + ASSERT (fullname != (Tcl_Obj*)(NULL)); + TypeLibUniqueID uid (ptl->m_libattr->guid, ptl->m_libattr->wMajorVerNum, ptl->m_libattr->wMinorVerNum); + m_loadedlibs.delete_entry(&(uid)); delete ptl; - m_loadedlibs.delete_entry(fullname); Tcl_DeleteHashEntry (pEntry); } +/* + *------------------------------------------------------------------------- + * TypeLibsTbl::GenerateNames -- + * Given a type library, generates the programmatic and full name for the + * library. + * + * Result: + * S_OK iff successful. + * + * Side Effects: + * The objects, progname and username allocate memory to store the + * names. + *------------------------------------------------------------------------- + */ +HRESULT TypeLibsTbl::GenerateNames (TObjPtr &progname, TObjPtr &username, ITypeLib *pLib) +{ + USES_CONVERSION; + ASSERT (pLib != NULL); + CComBSTR bprogname, busername; + HRESULT hr; + hr = pLib->GetDocumentation(-1, &bprogname, &busername, NULL, NULL); + if (FAILED(hr)) return hr; + + TLIBATTR * pattr = NULL; + hr = pLib->GetLibAttr (&pattr); + if (FAILED(hr)) return hr; + + ASSERT (pattr != NULL); + TDString str; + if (busername != NULL) + str << OLE2A(busername); + else + str << OLE2A(bprogname); + str << " (Ver " << pattr->wMajorVerNum << "." << pattr->wMinorVerNum << ")"; + pLib->ReleaseTLibAttr(pattr); + + username.create(); + username = str; + progname.create(); + progname = OLE2A(bprogname); + return hr; +} @@ -256,42 +355,7 @@ void TypeLibsTbl::UnloadLib (Tcl_Interp *pInterp, const char *fullname) */ TypeLib *TypeLibsTbl::EnsureCached (ITypeLib *ptl) { - USES_CONVERSION; - - ASSERT (ptl != NULL); - TDString verfullname; - TypeLib *pLib = NULL; - TLIBATTR *pattr = NULL; - HRESULT hr; - BSTR name = NULL, - fullname = NULL; - char *szname, *szfullname; - Tcl_HashEntry *pEntry = NULL; - CComPtr ptc; - - // get the libraries different names - hr = ptl->GetDocumentation(-1, &name, &fullname, NULL, NULL); - CHECKHR(hr); - szname = W2A(name); - szfullname = W2A(fullname); - FreeBSTR(name); - FreeBSTR(fullname); - if (find(szname, &pLib)) - return pLib; // cached already - - // build the fullname+version string - hr = ptl->GetLibAttr(&pattr); - CHECKHR(hr); - verfullname.set (szfullname) << " (Ver " << short(pattr->wMajorVerNum) << "." << - short(pattr->wMinorVerNum) << ")"; - ptl->ReleaseTLibAttr (pattr); pattr = NULL; - - // get the compiler interface - hr = ptl->GetTypeComp (&ptc); - CHECKHR(hr); - // now cache the lot - pLib = Cache (szname, verfullname, ptl, ptc); - return pLib; + return Cache(NULL, ptl); } @@ -314,7 +378,7 @@ TypeLib *TypeLibsTbl::EnsureCached (ITypeInfo *pInfo) UINT tmp; HRESULT hr; hr = pInfo->GetContainingTypeLib(&pLib, &tmp); - CHECKHR(hr); + if (FAILED(hr)) return NULL; return EnsureCached (pLib); } @@ -329,13 +393,17 @@ int TypeLib_Init (Tcl_Interp *pInterp) { OleInitialize(NULL); Tcl_CreateExitHandler (TypeLib_Exit, NULL); - Tcl_CreateObjCommand (pInterp, "typelib::loaded", TypeLib_LoadedLibs, NULL, NULL); - Tcl_CreateObjCommand (pInterp, "typelib::load", TypeLib_LoadLib, NULL, NULL); + Tcl_CreateObjCommand (pInterp, "typelib::loadedlibs", TypeLib_LoadedLibs, NULL, NULL); + Tcl_CreateObjCommand (pInterp, "typelib::_load", TypeLib_LoadLib, NULL, NULL); Tcl_CreateObjCommand (pInterp, "typelib::unload", TypeLib_UnloadLib, NULL, NULL); Tcl_CreateObjCommand (pInterp, "typelib::types", TypeLib_TypesInLib, NULL, NULL); Tcl_CreateObjCommand (pInterp, "typelib::typeinfo", TypeLib_TypeInfo, NULL, NULL); Tcl_CreateObjCommand (pInterp, "typelib::isloaded", TypeLib_IsLibLoaded, NULL, NULL); + Tcl_CreateObjCommand (pInterp, "typelib::reglib_path", TypeLib_GetRegLibPath, NULL, NULL); + Tcl_CreateObjCommand (pInterp, "typelib::loadedlib_path", TypeLib_GetLoadedLibPath, NULL, NULL); + Tcl_CreateObjCommand (pInterp, "typelib::loadedlib_details", TypeLib_GetDetails, NULL, NULL); + //// TESTS //// Tcl_CreateObjCommand (pInterp, "typelib::resolveconst", TypeLib_ResolveConstantTest, NULL, NULL); @@ -599,6 +667,8 @@ void TypeLib_GetName (ITypeLib *pLib, ITypeInfo *pInfo, TObjPtr &pname) bLibcreate = true; } // get the library programmatic name + + hr = pLib->GetDocumentation (-1, &progname, NULL, NULL, NULL); CHECKHR(hr); @@ -1203,10 +1273,30 @@ int TypeLib_DescribeTypeInfo (Tcl_Interp *pInterp, ITypeInfo *pti) default: presult = "???"; break; } + presult.lappend(methods).lappend(properties).lappend(inherited); - cmdresult = TCL_OK; + + + if (SUCCEEDED(pti->GetDocumentation (MEMBERID_NIL, NULL, &bdoc, NULL, NULL)) && bdoc != NULL) + { + presult.lappend (OLE2A(bdoc)); + SysFreeString (bdoc); + } + else + presult.lappend (""); + + LPOLESTR lpsz; + CHECKHR(StringFromCLSID (pta->guid, &lpsz)); + ASSERT (lpsz != NULL); + if (lpsz != NULL) { + presult.lappend(OLE2A (lpsz)); + CoTaskMemFree (lpsz); lpsz = NULL; + } } + Tcl_SetObjResult (pInterp, presult); + cmdresult = TCL_OK; + ReleaseTypeAttr (pti, pta); } catch (HRESULT hr) { @@ -1218,18 +1308,6 @@ int TypeLib_DescribeTypeInfo (Tcl_Interp *pInterp, ITypeInfo *pti) throw (error); } - if (cmdresult == TCL_OK) { - if (SUCCEEDED(pti->GetDocumentation (MEMBERID_NIL, NULL, &bdoc, NULL, NULL)) && bdoc != NULL) - { - presult.lappend (OLE2A(bdoc)); - SysFreeString (bdoc); - } - else - presult.lappend (""); - - Tcl_SetObjResult (pInterp, presult); - } - return cmdresult; } @@ -1522,7 +1600,7 @@ TCL_CMDEF(TypeLib_LoadedLibs) *------------------------------------------------------------------------- * TypeLib_LoadLib -- * Ensures that a given library is loaded. A library is described in terms - * of its full human-readable name. + * of its filename. * * Result: * TCL_OK iff successful. @@ -1534,14 +1612,16 @@ TCL_CMDEF(TypeLib_LoadedLibs) TCL_CMDEF(TypeLib_LoadLib) { if (objc != 2) { - Tcl_WrongNumArgs (pInterp, 1, objv, "full_libname"); + Tcl_WrongNumArgs (pInterp, 1, objv, "library_path"); return TCL_ERROR; } TObjPtr libname; libname.attach(objv[1], false); - if (g_libs.LoadLib (pInterp, libname) != NULL) + TypeLib * pLib = g_libs.LoadLib (pInterp, libname); + if (pLib) { + Tcl_SetResult (pInterp, pLib->m_progname, TCL_VOLATILE); return TCL_OK; - else + } else return TCL_ERROR; } @@ -1585,16 +1665,34 @@ TCL_CMDEF(TypeLib_UnloadLib) */ TCL_CMDEF(TypeLib_IsLibLoaded) { - if (objc != 2) { - Tcl_WrongNumArgs (pInterp, 1, objv, "fullname_library"); + USES_CONVERSION; + if (objc != 4) { + Tcl_WrongNumArgs (pInterp, 1, objv, "lib_guid majorver minorver"); return TCL_ERROR; } - TObjPtr name; - TObjPtr value; - value.create(false); - name.attach(objv[1]); - value = g_libs.IsLibLoaded(name); - Tcl_SetObjResult (pInterp, value); + GUID guid; + long maj, min; + + char * szguid = Tcl_GetStringFromObj (objv[1], NULL); + ASSERT (szguid != NULL); + if (FAILED(CLSIDFromString(A2OLE(szguid), &guid))) { + Tcl_SetResult (pInterp, "string isn't a guid", TCL_STATIC); + return TCL_ERROR; + } + + if (Tcl_GetLongFromObj(pInterp, objv[2], &maj) == TCL_ERROR) + return TCL_ERROR; + + if (Tcl_GetLongFromObj(pInterp, objv[3], &min) == TCL_ERROR) + return TCL_ERROR; + + TypeLib * pLib = NULL; + + pLib = g_libs.TypeLibFromUID(guid, maj, min); + Tcl_ResetResult (pInterp); + if (pLib) + Tcl_SetObjResult(pInterp, pLib->m_progname); + return TCL_OK; } @@ -1687,7 +1785,62 @@ TCL_CMDEF (TypeLib_TypesInLib) +HRESULT TypeLib_GetDefaultInterface (ITypeInfo *pti, bool bEventSource, ITypeInfo ** ppdefti) { + ASSERT (pti != NULL && ppdefti != NULL); + OptclTypeAttr attr; + attr = pti; + ASSERT (attr.m_pattr != NULL); + if (attr->typekind != TKIND_COCLASS) + return E_FAIL; + HRESULT hr; + WORD selected = -1; + + for (WORD index = 0; index < attr->cImplTypes; index++) { + + INT implflags; + hr = pti->GetImplTypeFlags(index, &implflags); + if (FAILED(hr)) return hr; + + if ( ((implflags & IMPLTYPEFLAG_FDEFAULT) == IMPLTYPEFLAG_FDEFAULT) && + ((bEventSource && (implflags & IMPLTYPEFLAG_FSOURCE) == IMPLTYPEFLAG_FSOURCE) || + (!bEventSource && (implflags & IMPLTYPEFLAG_FSOURCE) != (IMPLTYPEFLAG_FSOURCE))) + ) { + break; + } + } + if (index == attr->cImplTypes) + return E_FAIL; + + CComPtr pimpl; + HREFTYPE hreftype; + + // retrieve the referenced typeinfo + hr = pti->GetRefTypeOfImplType(index, &hreftype); + if (FAILED(hr)) return hr; + + hr = pti->GetRefTypeInfo(hreftype, &pimpl); + if (FAILED(hr)) return hr; + OptclTypeAttr pimplattr; + pimplattr = pimpl; + + // resolve typedefs + while (pimplattr->typekind == TKIND_ALIAS) { + CComPtr pref; + hr = pimpl->GetRefTypeInfo(pimplattr->tdescAlias.hreftype, &pref); + if (FAILED(hr)) return hr; + pimpl = pref; + pimplattr = pimpl; + } + + // if this isn't an interface forget it + if ((pimplattr->typekind != TKIND_DISPATCH) && + (pimplattr->typekind != TKIND_INTERFACE)) + return E_FAIL; + + // okay - return the typeinfo to the caller + return pimpl.CopyTo(ppdefti); +} @@ -1747,6 +1900,102 @@ TCL_CMDEF(TypeLib_TypeInfo) } + + + + + +TCL_CMDEF(TypeLib_GetRegLibPath) +{ + USES_CONVERSION; + if (objc != 4) { + Tcl_WrongNumArgs (pInterp, 1, objv, "lib_id majver minver"); + return TCL_ERROR; + } + + char * szGuid = Tcl_GetStringFromObj (objv[1], NULL); + long maj, min; + + if (Tcl_GetLongFromObj(pInterp, objv[2], &maj) == TCL_ERROR) + return TCL_ERROR; + + if (Tcl_GetLongFromObj(pInterp, objv[3], &min) == TCL_ERROR) + return TCL_ERROR; + + GUID guid; + if (FAILED(CLSIDFromString(A2OLE(szGuid), &guid))) { + Tcl_SetResult (pInterp, "failed to convert to a guid: ", TCL_STATIC); + Tcl_AppendResult (pInterp, szGuid, NULL); + return TCL_ERROR; + } + + CComBSTR path; + HRESULT hr = QueryPathOfRegTypeLib(guid, maj, min, LOCALE_SYSTEM_DEFAULT, &path); + if (FAILED(hr)) { + Tcl_SetResult (pInterp, HRESULT2Str(hr), TCL_DYNAMIC); + return TCL_ERROR; + } + Tcl_SetResult (pInterp, W2A(path), TCL_VOLATILE); + return TCL_OK; +} + +TCL_CMDEF(TypeLib_GetLoadedLibPath) +{ + if (objc != 2) { + Tcl_WrongNumArgs (pInterp, 1, objv, "progname"); + return TCL_ERROR; + } + char * szProgName = Tcl_GetStringFromObj(objv[1], NULL); + ASSERT (szProgName); + + TypeLib * plib = NULL; + g_libs.find(szProgName, &plib); + if (plib==NULL) { + Tcl_SetResult (pInterp, "couldn't find loaded library: ", TCL_STATIC); + Tcl_AppendResult (pInterp, szProgName, NULL); + return TCL_ERROR; + } + Tcl_SetObjResult (pInterp, plib->m_path); + return TCL_OK; +} + + +TCL_CMDEF(TypeLib_GetDetails) +{ + USES_CONVERSION; + if (objc != 2) { + Tcl_WrongNumArgs (pInterp, 1, objv, "progname"); + return TCL_ERROR; + } + char * szProgName = Tcl_GetStringFromObj (objv[1], NULL); + ASSERT(szProgName); + TypeLib * plib = NULL; + g_libs.find(szProgName, &plib); + if (plib == NULL) { + Tcl_SetResult (pInterp, "couldn't find loaded library: ", TCL_STATIC); + Tcl_AppendResult (pInterp, szProgName, NULL); + return TCL_ERROR; + } + TObjPtr obj; + obj.create(); + LPOLESTR pstr; + HRESULT hr; + hr = StringFromCLSID(plib->m_libattr->guid, &pstr); + if (FAILED(hr)) { + Tcl_SetResult (pInterp, HRESULT2Str(hr), TCL_DYNAMIC); + return TCL_ERROR; + } + obj.lappend(OLE2A(pstr)); + CoTaskMemFree(pstr); + obj.lappend(plib->m_libattr->wMajorVerNum); + obj.lappend(plib->m_libattr->wMinorVerNum); + obj.lappend(plib->m_path); + obj.lappend(plib->m_fullname); + Tcl_SetObjResult (pInterp, obj); + return TCL_OK; +} + + /* *------------------------------------------------------------------------- * TypeLib_ResolveName -- @@ -1761,6 +2010,7 @@ TCL_CMDEF(TypeLib_TypeInfo) * *------------------------------------------------------------------------- */ + void TypeLib_ResolveName (const char * lib, const char * type, TypeLib **pptl, ITypeInfo **ppinfo) { @@ -1884,7 +2134,7 @@ bool TypeLib_ResolveConstant (Tcl_Interp *pInterp, ITypeInfo *pti, ASSERT (bp.lpvardesc->lpvarValue != NULL); if (bp.lpvardesc->lpvarValue == NULL) throw ("constant didn't have a associated value!"); - var2obj (pInterp, *(bp.lpvardesc->lpvarValue), pObj); + var2obj (pInterp, *(bp.lpvardesc->lpvarValue), NULL, pObj); pti->ReleaseVarDesc (bp.lpvardesc); return true; } @@ -2006,3 +2256,4 @@ TCL_CMDEF(TypeLib_ResolveConstantTest) return TCL_ERROR; } + diff --git a/src/typelib.h b/src/typelib.h index 907709a..8e1610b 100644 --- a/src/typelib.h +++ b/src/typelib.h @@ -34,14 +34,55 @@ struct TypeLib { CComPtr m_ptl; CComPtr m_ptc; + TLIBATTR * m_libattr; + + TObjPtr m_progname, m_fullname, m_path; + + + TypeLib () { + m_progname.create(); + m_fullname.create(); + m_path.create(); + m_libattr = NULL; + } + + ~TypeLib () { + if (m_libattr != NULL) { + ASSERT (m_ptl != NULL); + m_ptl->ReleaseTLibAttr(m_libattr); + } + } + + HRESULT Init (ITypeLib *ptl, ITypeComp *ptc, const char * progname, + const char * fullname, const char * path) { + ASSERT (progname != NULL && fullname != NULL); - TypeLib (ITypeLib *ptl, ITypeComp *ptc) { m_ptl = ptl; m_ptc = ptc; + + m_progname = progname; + m_fullname = fullname; + if (path) + m_path = path; + else + m_path = "???"; + return ptl->GetLibAttr(&m_libattr); } }; +struct TypeLibUniqueID { + TypeLibUniqueID (const GUID & guid, WORD maj, WORD min) { + m_guid = guid; + m_majorver = maj; + m_minorver = min; + } + + GUID m_guid; + WORD m_majorver; + WORD m_minorver; +}; + // TypeLibsTbl - a hash table mapping library programmatic name to a TypeLib structure // Internally it also holds a mapping from the a libraries human readable name to @@ -51,19 +92,28 @@ class TypeLibsTbl : public THash public: TypeLibsTbl (); virtual ~TypeLibsTbl (); + void DeleteAll (); - ITypeLib* LoadLib (Tcl_Interp *pInterp, const char *fullname); - void UnloadLib (Tcl_Interp *pInterp, const char *fullname); - bool IsLibLoaded (const char *fullname); + + TypeLib* LoadLib (Tcl_Interp *pInterp, const char * fullpath); + void UnloadLib (Tcl_Interp *pInterp, const char * progname); + TypeLib* TypeLibFromUID (const GUID & guid, WORD maj, WORD min); + TypeLib* EnsureCached (ITypeLib *pLib); TypeLib* EnsureCached (ITypeInfo *pInfo); + + char* GetFullName (char * szProgName); + GUID* GetGUID (char * szProgName); + protected: // methods - TypeLib* Cache (const char *szname, const char *szfullname, ITypeLib *ptl, ITypeComp *ptc); + TypeLib* Cache (Tcl_Interp * pInterp, ITypeLib *ptl, const char * path = NULL); + HRESULT GenerateNames (TObjPtr &progname, TObjPtr &username, ITypeLib *pLib); protected: // properties - THash m_loadedlibs; // by name + THash m_loadedlibs; // by unique and full descriptor }; + // globals extern TypeLibsTbl g_libs; @@ -76,6 +126,6 @@ bool TypeLib_ResolveConstant (Tcl_Interp *pInterp, char *fullformatname, TObjPtr &pObj, ITypeInfo *pInfo = NULL); bool TypeLib_ResolveConstant (Tcl_Interp *pInterp, ITypeInfo *pti, const char *member, TObjPtr &pObj); - +HRESULT TypeLib_GetDefaultInterface (ITypeInfo *pti, bool bEventSource, ITypeInfo ** ppdefti); #endif // _TYPELIB_H_62518A80_624A_11d4_8004_0040055861F2 \ No newline at end of file diff --git a/src/typelib.tcl b/src/typelib.tcl index ac19136..7d8b00c 100644 --- a/src/typelib.tcl +++ b/src/typelib.tcl @@ -1,26 +1,78 @@ package require registry -package provide optcl 3.0 namespace eval typelib { - variable syslibs - variable syslibguids - array set syslibs {} - array set syslibguids {} + variable typelibraries + array set typelibraries {} + + + namespace export * + namespace import -force ::optcl_utils::* # ----------------------------------------------------------------------------- - # updatelibs -- called to enumerate and store the system libraries - proc updatelibs {} { - variable syslibs; - catch {unset syslibs} - array set syslibs {} + # latest_typelib_version -- + # For a given registered type library guid, it retrieves the most recent version + # number of the library, and returns a string giving a version qualified string + # description for the library. Returns {} iff failed. + # + proc latest_typelib_version {typelib_guid} { + set typelibpath HKEY_CLASSES_ROOT\\TypeLib\\$typelib_guid + if {[registry keys HKEY_CLASSES_ROOT\\TypeLib $typelib_guid] == {}} { + puts "couldn't find typelib $typelib" + return {} + } + set v [lindex [lsort -decreasing -real [registry keys $typelibpath]] 0] + + if {$v == {}} { + puts "bad typelib version number: $v for $typelib_guid" + set result {} + } else { + set result [makelibname $typelib $v] + } + return $result + } + + # makelibname -- + # standard function for creating a library's human readable name from the + # registered guid. + # + proc makelibname {typelib_guid ver} { + set maj 0 + set min 0 + scan $ver "%d.%d" maj min + return "[registry get HKEY_CLASSES_ROOT\\TypeLib\\$typelib_guid\\$ver {}] (Ver $maj.$min)" + } + + proc path_from_libname {libname} { + variable typelibraries + set r [array get typelibraries $libname] + if {$r == {}} { + error "library does not exist: $libname" + } + set libsettings [lindex $r 1] + return [eval path_from_libid $libsettings] + } + # updateLibs -- called to enumerate and store the system libraries + proc updateLibs {} { + variable typelibraries; + + # enumerate the current type libraries to make sure that they're still there + foreach library [array names typelibraries] { + try { + mset {name path} $typelibraries($library) + if {![file exists $path]} {throw {}} + } catch {er} { + unset typelibraries($library) + } + } + # now iterate over the registered type libraries in the system set root {HKEY_CLASSES_ROOT\TypeLib} foreach id [registry keys $root] { - catch { + try { foreach v [registry keys $root\\$id] { scan $v "%d.%d" maj min; if [catch { @@ -32,60 +84,93 @@ namespace eval typelib { continue; } - set name "[registry get $root\\$id\\$v {}] (Ver $maj.$min)" - set syslibs($name) [list $id $maj $min] + set name [makelibname $id $maj.$min] + set path [typelib::reglib_path $id $maj $min] + addLibrary $name $id $maj $min $path } + } catch {e} { + puts $e } } } - # ----------------------------------------------------------------------------- - # categories -- returns the component categories - proc categories {} { + proc addLibrary {name typelib_id maj min path} { + variable typelibraries + set typelibraries([list [string toupper $typelib_id] $maj $min]) [list $name $path] + } - set alldata {} - set k "HKEY_CLASSES_ROOT\\Component Categories" - set cats [registry keys $k] + proc persistLoaded {} { + set cmd "typelib::loadLibsFromDetails" + lappend cmd [getLoadedLibsDetails] + return $cmd + } - foreach cat $cats { - set values [registry values $k\\$cat] - set data {} - foreach value $values { - lappend data [registry get $k\\$cat $value] - } - lappend alldata $data + # getLoadedLibsDetails -- + # Retrieves a list of descriptors for the current loaded libraries + proc getLoadedLibsDetails {} { + set result {} + foreach progname [typelib::loadedlibs] { + lappend result [typelib::loadedlib_details $progname] } + return $result + } - return $alldata + proc loadLibsFromDetails {details} { + foreach libdetail $details { + loadLibFromDetail $libdetail + } } + proc loadLibFromDetail {libdetail} { + variable typelibraries + mset {guid maj min path fullname} $libdetail + # if the library is already registered, get the path from the registry + mset { _ regpath} [lindex [array get typelibraries [list $guid $maj $min]] 1] + if {$regpath != {}} { + set path $regpath + } + + typelib::load $path + addLibrary $fullname $guid $maj $min $path + } + + proc load {path} { + set progname [typelib::_load $path] + mset {guid maj min path fullname} [typelib::loadedlib_details $progname] + addLibrary $fullname $guid $maj $min $path + return $progname + } # ----------------------------------------------------------------------------- - # libdetail -- returns a the id, maj and min version number - # in a list if it exists, else throws an error + + # libdetail -- + # returns the id, maj and min version numbers and + # the path as a list if they exists, else throws an error. + # proc libdetail {name} { - variable syslibs + variable typelibraries - if {[array names syslibs $name] == {}} { + if {[array names typelibraries $name] == {}} { error "could not find the library '$name'" } - return [lindex [array get syslibs $name] 1] + return [lindex [array get typelibraries $name] 1] } #------------------------------------------------------------------------------ - # alllibs -- returns all the registered libraries by name + # alllibs -- returns all the registered libraries by {guid maj min} identification proc alllibs {} { - variable syslibs - return [array names syslibs] + variable typelibraries + return [array names typelibraries] } + # returns the fully qualified default interface for a com class proc defaultinterface {classtype} { set desc [typelib::typeinfo $classtype] if {[llength $desc] != 3} { @@ -101,522 +186,174 @@ namespace eval typelib { } #------------------------------------------------------------------------------ - updatelibs - } +namespace eval COM { + namespace import -force ::typelib::* -if {[info commands tk] != {}} { - namespace eval tlview { - catch {font delete tlviewertext} - catch {font delete tlviewerhigh} - catch {font delete tlviewerbold} - font create tlviewertext -family Arial -size 9 -weight normal - font create tlviewerhigh -family Arial -size 9 -weight bold - font create tlviewerbold -family Arial -size 9 -weight bold - - variable bgcolor white - variable textcolor black - variable highlightcolor blue - variable selectcolor red - variable labelcolor red - - array set viewedtypes {} - - #------------------------------------------------------------------------------ - proc scrltxt {w {sb {x y}}} { - variable bgcolor; - frame $w -bd 2 -relief sunken; - - text $w.t -bg $bgcolor -bd 0 -relief flat -cursor arrow -width 40 -height 20 - grid $w.t -column 0 -row 0 -sticky nsew; + # categories + # retrieve a list of all category names + proc categories {} { + set alldata {} + set k "HKEY_CLASSES_ROOT\\Component Categories" + set cats [registry keys $k] - if {[lsearch $sb x] >= 0} { - scrollbar $w.x -orient horizontal -command [list $w.t xview] - $w.t config -xscrollcommand [list $w.x set] -wrap none - grid $w.x -column 0 -row 1 -sticky ew; - } - if {[lsearch $sb y] >= 0} { - scrollbar $w.y -orient vertical -command [list $w.t yview] - $w.t config -yscrollcommand [list $w.y set] - grid $w.y -column 1 -row 0 -sticky ns; + foreach cat $cats { + set values [registry values $k\\$cat] + set data {} + foreach value $values { + lappend data [registry get $k\\$cat $value] } - - grid columnconfigure $w 0 -weight 1; - grid rowconfigure $w 0 -weight 1; - } - - - #------------------------------------------------------------------------------ - proc cl_list {w} { - variable bgcolor - frame $w -bd 2 -relief sunken - canvas $w.c -yscrollcommand "$w.v set" -xscrollcommand "$w.h set" -bd 0 -relief flat -cursor arrow -bg $bgcolor -highlightthickness 0 - scrollbar $w.h -orient horizontal -command "$w.c xview" - scrollbar $w.v -orient vertical -command "$w.c yview" - - grid $w.c -column 0 -row 0 -sticky news - grid $w.h -column 0 -row 1 -sticky ew - grid $w.v -column 1 -row 0 -sticky ns - grid columnconfigure $w 0 -weight 1 - grid rowconfigure $w 0 -weight 1 - bind $w.c <1> { focus %W } - bind $w.c { %W yview scroll -1 pages} - bind $w.c { %W yview scroll 1 pages} - return $w + lappend alldata $data } + return $alldata + } - proc cl_list_update {w} { - variable ::typelib::syslibs - variable bgcolor - - if {![winfo exists $w]} { - error "expected to find a TypeLib list widget: $w" - } - - set c $w.c - $c delete all + # collate all the category names under the category clsid (parameter 1) into an + # array passed by name + proc collate_category_names {category arrname} { + upvar $arrname categories - foreach tl [lsort [array names ::typelib::syslibs]] { - cl_list_addlib $w $tl + set ck "HKEY_CLASSES_ROOT\\Component Categories\\$category" + catch { + foreach value [registry values $ck] { + catch {set categories([registry get $ck $value]) ""} } - } - - - - proc cl_list_addlib {w tl} { - variable bgcolor - - set c $w.c - set bbox [$c bbox entry] - if {$bbox == {}} {set bbox {0 0 10 10}} - set bottom [lindex $bbox 3] - set bottom [expr int($bottom) + 3] - set tag [$c create text 10 $bottom -anchor nw -fill black -font tlviewertext -justify left -text $tl -tags entry] - $c bind $tag <1> [namespace code "cl_list_press $w $tag"] - cl_list_updatetag $w $tag + } err + return $err + } - set bbox [$c bbox entry] - set sr [list 0 0 [lindex $bbox 2] [expr $bottom + 20]] - $c config -scrollregion $sr - } + # collates all categories for a given clsid in an array that is passed by name + proc clsid_categories_to_array {clsid arrname} { + upvar $arrname categories + set k "HKEY_CLASSES_ROOT\\CLSID\\$clsid" + + # catch if there aren't any implemented categories - proc cl_list_updatetag {w tag} { - variable textcolor - variable highlightcolor + foreach subkey [registry keys "HKEY_CLASSES_ROOT\\CLSID\\$clsid"] { + switch $subkey { + {Implemented Categories} { + foreach category [registry keys "$k\\$subkey"] { + collate_category_names $category categories + } + } - set c $w.c - set tl [$c itemcget $tag -text] + Programmable { + array set categories {{Automation Objects} {}} + } - if {![typelib::isloaded $tl]} { - $c itemconfig $tag -fill $textcolor -font tlviewertext - } else { - $c itemconfig $tag -fill $highlightcolor -font tlviewerhigh - } - } + Control { + array set categories {Controls {}} + } + DocObject { + array set categories {{Document Objects} {}} + } - proc cl_list_press {w tag} { - set c $w.c - set tl [$c itemcget $tag -text] - set parent [winfo parent $w] - - if {![typelib::isloaded $tl]} { - # loading typelib - if {[catch {typelib::load $tl} progname]} { - puts $progname - $parent.error config -text [string trim $progname] - } else { - puts "loaded $progname" - $parent.error config -text "loaded $progname" - loadedlibs_updateall + Insertable { + array set categories {{Embeddable Objects} {}} + } } - } else { - typelib::unload $tl - puts "unloaded $tl" - $parent.error config -text "unloaded $tl" - loadedlibs_updateall } - - cl_list_updatetag $w $tag - } - - - - proc refview {w} { - toplevel $w - wm title $w "Referenced Type Libraries" - bind $w "destroy $w" - bind $w "$w.close invoke" - bind $w "$w.refresh config -relief sunken; update; $w.refresh invoke; $w.refresh config -relief raised" - button $w.close -text Close -width 7 -command "destroy $w" -underline 0 - button $w.refresh -text Refresh -width 7 -command [namespace code "cl_list_update $w.list"] -underline 0 - label $w.error -bd 1 -relief sunken - - grid [cl_list $w.list] -column 0 -row 0 -columnspan 2 -sticky nsew - grid $w.close -column 0 -row 1 -padx 5 -pady 5 - grid $w.refresh -column 1 -row 1 -padx 5 -pady 5 - grid $w.error -column 0 -row 2 -columnspan 2 -sticky nsew - - grid columnconfig $w 0 -weight 1 - grid columnconfig $w 1 -weight 1 - grid rowconfig $w 0 -weight 1 - - cl_list_update $w.list - return $w - } - - - #------------------------------------------------------------------------------ - - proc loadedlibs_updateall {} { - foreach w [winfo child .] { - if {[string compare [winfo class $w] TLLoadedTypeLibs] == 0} { - loadedlibs_update $w - } - } - } - - proc loadedlibs_update {w} { - variable bgcolor - variable textcolor - variable highlightcolor - - $w.l.t config -state normal - $w.l.t delete 1.0 end - foreach lib [lsort [typelib::loaded]] { - $w.l.t tag configure tag$lib -foreground $highlightcolor -font tlviewertext -underline 0 - $w.l.t insert end "$lib\n" tag$lib - $w.l.t tag bind tag$lib <1> [namespace code "viewlib $lib"] - $w.l.t tag bind tag$lib "$w.l.t config -cursor hand2; $w.l.t tag config tag$lib -underline 1" - $w.l.t tag bind tag$lib "$w.l.t config -cursor arrow; $w.l.t tag config tag$lib -underline 0" - } - $w.l.t config -state disabled - } - - proc loadedlibs {w} { - toplevel $w -class TLLoadedTypeLibs - - wm title $w "Loaded Libraries" - scrltxt $w.l - - grid $w.l -column 0 -row 0 -sticky nsew - grid columnconfig $w 0 -weight 1 - grid rowconfig $w 0 -weight 1 - loadedlibs_update $w - bind $w [namespace code "loadedlibs_update $w"] - } + } - #------------------------------------------------------------------------------ - proc viewlib_onenter {txt tag} { - $txt config -cursor hand2 - $txt tag config $tag -underline 1 - } + # retrieves, as a list, the categories for the given clsid + proc clsid_categories {clsid} { + array set categories {} + clsid_categories_to_array $clsid categories + return [array names categories] + } - proc viewlib_onleave {txt tag} { - $txt config -cursor arrow - $txt tag config $tag -underline 0 - } - proc viewlib_unselect {txt lib} { - variable viewedtypes - variable textcolor - if {[array name viewedtypes $lib] != {}} { - set type $viewedtypes($lib) - $txt tag config tag$type -foreground $textcolor -font tlviewertext - set viewedtypes($lib) {} - } + # retrieves all clsids that match the category name given by the first parameter + proc clsids {{cat {}}} { + array set categories {} + set clsidk "HKEY_CLASSES_ROOT\\CLSID" + if {$cat == {}} { + return [registry keys $clsidk] } + # else ... - - proc viewlib_select {fulltype } { - variable viewedtypes - variable highlightcolor - - puts $fulltype - set sp [split $fulltype .] - if {[llength $sp] != 2} { - return - } - - set lib [lindex $sp 0] - set type [lindex $sp 1] - - set w [viewlib $lib] - set txt $w.types.t - - viewlib_unselect $txt $lib - $txt tag config tag$type -foreground $highlightcolor -font tlviewerhigh - - $txt see [lindex [$txt tag ranges tag$type] 0] - set viewedtypes($lib) $type - viewlib_readelems $w $lib $type; - } + set classes {} - - proc viewlib_selectelem {w fulltype element} { - variable viewedtypes - variable highlightcolor - - puts "$fulltype $element" - set sp [split $fulltype .] - set lib [lindex $sp 0] - set type [lindex $sp 1] - - set txt $w.elems.t - - viewlib_unselect $txt $fulltype - $txt tag config tag$element -foreground $highlightcolor -font tlviewerhigh - $txt see [lindex [$txt tag ranges tag$element] 0] - set viewedtypes($fulltype) $element - viewlib_readdesc $w $lib $type $element - } - - ### - # creates a list of types in some library - proc viewlib_readtypes {w lib} { - variable textcolor - set txt $w.types.t - - $txt config -state normal - $txt del 1.0 end - - foreach tdesc [lsort [typelib::types $lib]] { - $txt insert end "[lindex $tdesc 0]\t" - set full [lindex $tdesc 1] - set type [lindex [split $full .] 1] - $txt tag configure tag$type -foreground $textcolor -font tlviewertext -underline 0 - $txt insert end "$type\n" tag$type - $txt tag bind tag$type <1> [namespace code " - viewlib_select $full; - "] - - $txt tag bind tag$type [namespace code "viewlib_onenter $txt tag$type"] - $txt tag bind tag$type [namespace code "viewlib_onleave $txt tag$type"] - } - $txt config -state disabled - } - - - proc viewlib_writetype {txt fulltype} { - variable highlightcolor - if {[llength [split $fulltype .]] > 1} { - $txt tag configure tag$fulltype -foreground $highlightcolor -font tlviewertext -underline 0 - $txt tag bind tag$fulltype [namespace code "viewlib_onenter $txt tag$fulltype"] - $txt tag bind tag$fulltype [namespace code "viewlib_onleave $txt tag$fulltype"] - $txt tag bind tag$fulltype <1> [namespace code "viewlib_select $fulltype"] - $txt insert end "$fulltype" tag$fulltype - } else { - $txt insert end "$fulltype" + foreach clsid [registry keys $clsidk] { + catch [unset categories] + array set categories {} + clsid_categories_to_array $clsid categories + if {[array names categories $cat]!={}} { + lappend classes $clsid } } + return $classes + } - ### - # displays the elements for a type of some library - proc viewlib_readelems {w lib type} { - variable labelcolor - variable textcolor - variable highlightcolor - - set txt $w.elems.t - $txt config -state normal - $txt del 1.0 end - set elems [typelib::typeinfo $lib.$type] - loadedlibs_updateall - - $txt tag configure label -font tlviewerhigh -underline 1 -foreground $labelcolor - - if {[string compare "typedef" [lindex $elems 0]] == 0} { - # --- we are working with a typedef - set t [lindex $elems 3] - $txt insert end "Typedef\n\t" label - viewlib_writetype $txt $t - } else { - if {[llength [lindex $elems 1]] != 0} { - $txt insert end "Methods\n" label - } - - foreach method [lsort [lindex $elems 1]] { - $txt tag configure tag$method -foreground $textcolor -font tlviewertext -underline 0 - $txt tag bind tag$method [namespace code "viewlib_onenter $txt tag$method"] - $txt tag bind tag$method [namespace code "viewlib_onleave $txt tag$method"] - $txt tag bind tag$method <1> [namespace code "viewlib_selectelem $w $lib.$type $method"] - $txt insert end "\t$method\n" tag$method - } - - if {[llength [lindex $elems 2]] != 0} { - $txt insert end "Properties\n" label - } - - foreach prop [lsort [lindex $elems 2]] { - $txt tag configure tag$prop -foreground $textcolor -font tlviewertext -underline 0 - $txt tag bind tag$prop [namespace code "viewlib_onenter $txt tag$prop"] - $txt tag bind tag$prop [namespace code "viewlib_onleave $txt tag$prop"] - $txt tag bind tag$prop <1> [namespace code "viewlib_selectelem $w $lib.$type $prop"] - $txt insert end "\t$prop\n" tag$prop - } - if {[llength [lindex $elems 3]] != 0} { - $txt insert end "Inherited Types\n" label - } + # provides a description for the clsid + proc describe_clsid {clsid} { + set clsidk "HKEY_CLASSES_ROOT\\CLSID\\$clsid" + return [registry get $clsidk {}] + } - foreach impl [lsort -index 1 [lindex $elems 3]] { - # implemented interfaces - set t [lindex $impl 1] - set flags [lindex $impl 0] - if {[lsearch -exact $flags default] != -1} { - $txt insert end "*" - } - if {[lsearch -exac $flags source] != -1} { - $txt insert end "event" - } - $txt insert end "\t" - - $txt tag configure itag$t -foreground $highlightcolor -font tlviewertext -underline 0 - $txt tag bind itag$t [namespace code "viewlib_onenter $txt itag$t"] - $txt tag bind itag$t [namespace code "viewlib_onleave $txt itag$t"] - $txt tag bind itag$t <1> [namespace code "viewlib_select $t"] - - $txt insert end "$t\n" itag$t - } - } - $txt config -state disabled - viewlib_settypedoc $w [lindex $elems 4] - } - - proc viewlib_settypedoc {w doc} { - set txt $w.desc.t - $txt config -state normal - $txt delete 1.0 end - $txt insert end $doc - $txt config -state disabled + # retrieves a list of clsid descriptor for all clsids that have the category specified by + # parameter one. If parameter is {} then all clsids are returned. + proc describe_all_clsids {{cat {}}} { + set l {} + foreach clsid [categories::all_clsids $cat] { + lappend l [categories::describe_clsid $clsid] } + return [lsort -dictionary $l] + } + # retrieve the programmatics identifier for a clsid. + # If any exist, the result of this procedure is the programmatic identifier for the + # the clsid, followed by an optional version independent identifier + proc progid_from_clsid {clsid} { + set clsidk "HKEY_CLASSES_ROOT\\CLSID\\$clsid" + set progid {} + set verindid {} + catch {set progid [registry get "$clsidk\\ProgID" {}]} + catch {lappend progid [registry get "$clsidk\\VersionIndependentProgID" {}]} + return $progid + } - ### - # retrieves the description for an element - proc viewlib_readdesc {w lib type elem} { - variable labelcolor - - set txt $w.desc.t - $txt config -state normal - $txt delete 1.0 end - - $txt tag configure label -font tlviewerhigh -underline 1 -foreground $labelcolor - $txt tag configure element -font tlviewerbold - $txt tag bind element [namespace code "viewlib_onenter $txt element"] - $txt tag bind element [namespace code "viewlib_onleave $txt element"] - - $txt tag bind element <1> [namespace code "viewlib_select $lib.$type; viewlib_selectelem $w $lib.$type $elem"] - - set desc [typelib::typeinfo $lib.$type $elem] - set kind [lindex $desc 0] - switch $kind { - property { - $txt insert end "Property" label - $txt insert end "\t[lindex $desc 2]\n" - - set p [lindex $desc 1] - # insert the flags - $txt insert end "[lindex $p 0]\t" - viewlib_writetype $txt [lindex $p 1] - $txt insert end " " - $txt insert end "[lindex $p 2]" element - - set params [lrange $p 3 end] - - foreach param $params { - $txt insert end "\n\t" - - if {[llength $param] == 3} { - $txt insert end "[lindex $param 0]\t" - set param [lrange $param 1 end] - } - viewlib_writetype $txt [lindex $param 0] - $txt insert end " [lrange $param 1 end]" - } - } - - method { - set m [lindex $desc 1] - $txt insert end "Method" label - $txt insert end "\t[lindex $desc 2]\n" - viewlib_writetype $txt [lindex $m 0] - $txt insert end " " - $txt insert end "[lindex $m 1]" element - set params [lrange $m 2 end] - - foreach param $params { - $txt insert end "\n\t" - - if {[llength $param] == 3} { - $txt insert end "[lindex $param 0]\t" - set param [lrange $param 1 end] - } - viewlib_writetype $txt [lindex $param 0] - $txt insert end " [lrange $param 1 end]" - } - } - } - puts [lindex $desc 1] - $txt config -state disabled + proc typelib_from_clsid {clsid} { + set clsidk "HKEY_CLASSES_ROOT\\CLSID\\$clsid" + # associated typelib? + + if {[registry keys $clsidk TypeLib] == {}} { + return {} } + set typelib [registry get $clsidk\\TypeLib {}] - - #### - # Creates a viewer for library - proc viewlib {lib} { - set w ._tlview$lib - if [winfo exists $w] { - raise $w - return $w - } - toplevel $w -class tlview_$lib - wm title $w "Type Library: $lib" - - label $w.tl -text Types; - label $w.el -text Elements; - label $w.dl -text Description; - - scrltxt $w.types; - scrltxt $w.elems - scrltxt $w.desc y - $w.desc.t config -height 5 - $w.desc.t config -state disabled - $w.elems.t config -state disabled - $w.types.t config -state disabled - - grid $w.tl -column 0 -row 0 -sticky nw - grid $w.types -column 0 -row 1 -sticky news -padx 2 -pady 2 - grid $w.el -column 1 -row 0 -sticky nw - grid $w.elems -column 1 -row 1 -sticky news -padx 2 -pady 2 - grid $w.dl -column 0 -row 2 -sticky nw - grid $w.desc -column 0 -row 3 -columnspan 2 -sticky news -padx 2 -pady 2 - - grid columnconfigure $w 0 -weight 1 - grid columnconfigure $w 1 -weight 1 - grid rowconfigure $w 1 -weight 1 - #grid rowconfigure $w 3 -weight 1 - - viewlib_readtypes $w $lib - return $w + # does it exist? + if {[registry keys HKEY_CLASSES_ROOT\\TypeLib $typelib] == {}} { + puts "couldn't find typelib $typelib from clsid $clsid" + return {} } - - proc viewtype {fullname} { - viewlib_select $fullname + # do we have a version number?? + if {[registry keys $clsidk Version] != {}} { + set ver [registry get $clsidk\\Version {}] + set result [makelibname $typelib $ver] + } elseif {[registry keys $clsidk VERSION] != {}} { + set ver [registry get $clsidk\\VERSION {}] + set result [makelibname $typelib $ver] + } else { + # get the latest version of the type library + set result [latest_typelib_version $typelib] } + return $result } -} \ No newline at end of file +} diff --git a/src/utility.cpp b/src/utility.cpp index ea4bba6..5c41a1f 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -31,6 +31,10 @@ #include "typelib.h" #include "optclobj.h" #include "optcltypeattr.h" +#include "optclbindptr.h" +#include "comrecordinfoimpl.h" +#include + #ifdef _DEBUG /* @@ -63,6 +67,35 @@ void OptclTrace(LPCTSTR lpszFormat, ...) #endif //_DEBUG + +template +class TCoMem { +public: + TCoMem () : p(NULL) {} + ~TCoMem () { + Free(); + } + + void Free () { + if (p) { + CoTaskMemFree(p); + p = NULL; + } + } + + T* Alloc (ULONG size) { + Free(); + p = (T*)(CoTaskMemAlloc (size)); + return p; + } + + operator T* () { + return p; + } +protected: + T * p; +}; + /* *------------------------------------------------------------------------- * HRESULT2Str -- @@ -317,7 +350,7 @@ void OptclVariantClear (VARIANT *pvar) -bool var2obj_byref (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclObj **ppObj) +bool var2obj_byref (Tcl_Interp *pInterp, VARIANT &var, ITypeInfo *pti, TObjPtr &presult, OptclObj **ppObj) { ASSERT (var.ppunkVal != NULL); @@ -327,7 +360,7 @@ bool var2obj_byref (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclOb BSTR bstr = NULL; HRESULT hr = S_OK; OptclObj * pObj = NULL; - + ULONG size = 0; presult.create(); if (var.ppunkVal == NULL) { @@ -340,7 +373,7 @@ bool var2obj_byref (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclOb case VT_DISPATCH: case VT_UNKNOWN: if (*var.ppunkVal != NULL) { - pObj = g_objmap.Add (pInterp, *var.ppunkVal); + pObj = g_objmap.Add (pInterp, *var.ppunkVal, pti); presult = (const char*)(*pObj); // cast to char* if (ppObj != NULL) *ppObj = pObj; @@ -385,9 +418,12 @@ bool var2obj_byref (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclOb Tcl_SetResult (pInterp, "pointer to null", TCL_STATIC); bOk = false; } else { - bOk = var2obj (pInterp, *var.pvarVal, presult, ppObj); + bOk = var2obj (pInterp, *var.pvarVal, NULL, presult, ppObj); } break; + case VT_RECORD: + return record2obj(pInterp, var, presult); + break; default: presult = "?unhandledtype?"; } @@ -404,6 +440,125 @@ bool var2obj_byref (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclOb } +/* + *------------------------------------------------------------------------- + * record2obj + * Converts a VT_RECORD variant to a Tcl object + * Result: + * true iff successful + * Side Effects: + * Can create new optcl objects, which without reference counting might + * become a nightmare! :-( + *------------------------------------------------------------------------- + */ +bool record2obj (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &result) +{ + USES_CONVERSION; + ASSERT (var.vt == VT_RECORD && var.pRecInfo != NULL); + + ULONG fields = 0, index; + TCoMem fieldnames; + bool bok = true; + + + IRecordInfo *prinfo = var.pRecInfo; + CComPtr pinf; + + try { + CHECKHR(prinfo->GetTypeInfo(&pinf)); + CHECKHR(prinfo->GetFieldNames (&fields, NULL)); + if (fieldnames.Alloc (fields) == NULL) + throw "failed to allocate memory."; + CHECKHR(prinfo->GetFieldNames (&fields, fieldnames)); + + for (index = 0; bok && index < fields; index++) { + CComVariant varValue; + TObjPtr value; + CHECKHR(prinfo->GetField(var.pvRecord, fieldnames[index], &varValue)); + result.lappend(OLE2A(fieldnames[index])); + bok = var2obj (pInterp, varValue, pinf, value, NULL); + if (bok) + result.lappend(value); + } + + } catch (HRESULT hr) { + Tcl_SetResult (pInterp, HRESULT2Str(hr), TCL_DYNAMIC); + bok = false; + } catch (_com_error ce) { + Tcl_SetResult (pInterp, T2A((TCHAR*)ce.ErrorMessage()), TCL_VOLATILE); + bok = false; + } catch (char *err) { + Tcl_SetResult (pInterp, err, TCL_VOLATILE); + bok = false; + } catch (...) { + Tcl_SetResult (pInterp, "An unexpected error type occurred", TCL_STATIC); + bok = false; + } + + if (fieldnames != NULL) { + for (index = 0; index < fields; index++) + SysFreeString(fieldnames[index]); + } + return bok; +} + + + + +bool vararray2obj (Tcl_Interp * pInterp, VARIANT &var, ITypeInfo * pti, TObjPtr &presult) +{ + bool bOk = false; + LONG lbound, ubound; + VARTYPE vt = var.vt & (~VT_ARRAY); // type of elements array + + presult.create(); + + if (var.parray == NULL) { + Tcl_SetResult (pInterp, "invalid pointer to COM safe array", TCL_STATIC); + return false; + } + + + ULONG dims = SafeArrayGetDim (var.parray), // total number of dimensions + dindex; // dimension iterator + auto_array lbounds(dims), ubounds(dims); + + // get the lower and upper bounds of each dimension + for (dindex = 0; dindex < dims; dindex++) { + CHECKHR_TCL(SafeArrayGetLBound(var.parray, dindex, lbounds+dindex), pInterp, false); + CHECKHR_TCL(SafeArrayGetUBound(var.parray, dindex, ubounds+dindex), pInterp, false); + } + + + + CHECKHR_TCL(SafeArrayGetLBound(var.parray, 0, &lbound), pInterp, false); + CHECKHR_TCL(SafeArrayGetUBound(var.parray, 0, &ubound), pInterp, false); + + for (LONG index = lbound; index <= ubound; index++) { + CComVariant varElement; + varElement.vt = vt; + + // WARNING: The following code is *not* solid, as it doesn't handle record structures at all!! + // in order to do this, I'll have to take into account the type info associate with this + // array ... not now I guess. + if (vt == VT_VARIANT) { + CHECKHR_TCL(SafeArrayGetElement(var.parray, &index, &varElement), pInterp, false); + } else { + CHECKHR_TCL(SafeArrayGetElement(var.parray, &index, &(varElement.punkVal)), pInterp, false); + } + TObjPtr element; + // now that we've got the variant, convert it to a tcl object + if (!var2obj (pInterp, varElement, pti, element)) + return false; + // append it to the result + presult += element; + } + + return bOk; +} + + + /* *------------------------------------------------------------------------- * var2obj -- @@ -414,7 +569,7 @@ bool var2obj_byref (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclOb * None. *------------------------------------------------------------------------- */ -bool var2obj (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclObj **ppObj /* = NULL*/) +bool var2obj (Tcl_Interp *pInterp, VARIANT &var, ITypeInfo *pti, TObjPtr &presult, OptclObj **ppObj /* = NULL*/) { USES_CONVERSION; @@ -430,17 +585,16 @@ bool var2obj (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclObj **pp if ((var.vt & VT_ARRAY) || (var.vt & VT_VECTOR)) { - Tcl_SetResult (pInterp, "can't handle arrays or vectors for now", TCL_STATIC); - return false; + return vararray2obj (pInterp, var, pti, presult); } if (var.vt == VT_VARIANT) { ASSERT (var.pvarVal != NULL); - return var2obj (pInterp, *(var.pvarVal), presult, ppObj); + return var2obj (pInterp, *(var.pvarVal), pti, presult, ppObj); } if (var.vt & VT_BYREF) - return var2obj_byref (pInterp, var, presult, ppObj); + return var2obj_byref (pInterp, var, pti, presult, ppObj); presult.create(); @@ -454,6 +608,10 @@ bool var2obj (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclObj **pp presult = (const char*)(*pObj); // cast to char* if (ppObj != NULL) *ppObj = pObj; + if (pti != NULL) { + g_libs.EnsureCached (pti); + pObj->SetInterfaceFromType(pti); + } } else presult = 0; @@ -473,6 +631,9 @@ bool var2obj (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclObj **pp case VT_R8: presult = (double)(var.dblVal); break; + case VT_RECORD: + return record2obj (pInterp, var, presult); + break; default: // standard string conversion required comvar = var; name = comvar; @@ -507,6 +668,7 @@ bool var2obj (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclObj **pp * * Result: * true iff successful, else interpreter holds error string. + * * Side effects: * None. *------------------------------------------------------------------------- @@ -519,7 +681,6 @@ bool obj2var_ti (Tcl_Interp *pInterp, TObjPtr &obj, VARIANT &var, ASSERT (pInterp != NULL); OptclTypeAttr ota; - CComPtr pcurrent; CComPtr ptmpunk; HRESULT hr; TObjPtr ptmp; @@ -527,115 +688,110 @@ bool obj2var_ti (Tcl_Interp *pInterp, TObjPtr &obj, VARIANT &var, OptclObj * pOptclObj = NULL; long lValue; + // if we have no value, set the variant to an empty + if (obj.isnull ()) { + VariantClear(&var); + return true; + } + // if no type description has been provided, do a simple conversion if (pdesc == NULL) { obj2var (obj, var); bOk = true; } - // a simple type - else if (pdesc->vt != VT_USERDEFINED && pdesc->vt != VT_SAFEARRAY) { - if (pdesc->vt != VT_PTR) - return obj2var_vt (pInterp, obj, var, pdesc->vt); - else { - ASSERT (pdesc->lptdesc->vt != VT_PTR && - pdesc->lptdesc->vt != VT_USERDEFINED && - pdesc->lptdesc->vt != VT_SAFEARRAY); - - if (pdesc->lptdesc->vt == VT_PTR || - pdesc->lptdesc->vt == VT_USERDEFINED || - pdesc->lptdesc->vt == VT_SAFEARRAY) - { - Tcl_SetResult (pInterp, "can't convert - optcl doesn't support level of de-referencing", TCL_STATIC); - return false; - } - return obj2var_vt_byref (pInterp, obj, var, pdesc->lptdesc->vt); - } - } - - // arrays - should be easy to do - not enough time right now... else if (pdesc->vt == VT_SAFEARRAY) { // wont do arrays for now. Tcl_SetResult (pInterp, "optcl doesn't currently handle array types", TCL_STATIC); } - else { + else if (pdesc->vt == VT_USERDEFINED) { // type information provided and it refers to a user defined type // resolve the initial type + CComPtr refinfo; + CHECKHR(pInfo->GetRefTypeInfo (pdesc->hreftype, &refinfo)); - hr = pInfo->GetRefTypeInfo (pdesc->hreftype, &ota.m_pti); - CHECKHR(hr); - g_libs.EnsureCached (ota.m_pti); - hr = ota.GetTypeAttr(); - CHECKHR(hr); + if (!TypeInfoResolveAliasing (pInterp, refinfo, &ota.m_pti)) + return false; + CHECKHR(ota.GetTypeAttr()); ASSERT (ota.m_pattr != NULL); - pcurrent = pInfo; - while (ota->typekind == TKIND_ALIAS && - ota->tdescAlias.vt == VT_USERDEFINED) - { - HREFTYPE href = ota->tdescAlias.hreftype; - pcurrent = ota.m_pti; - ota = NULL; // release the type attribute and type info - pcurrent->GetRefTypeInfo (href, &ota.m_pti); - hr = ota.GetTypeAttr(); - CHECKHR(hr); - } - + // we've now climbed back up the alias chain and have one of the following: // enum, record, module, interface, dispatch, coclass, union or alias to a basic type - // The following we can't (currently) do anything useful with: record, union, module. - if (ota.m_pattr->typekind == TKIND_ALIAS) - return obj2var_ti (pInterp, obj, var, pcurrent, &(ota->tdescAlias)); + if (ota.m_pattr->typekind == TKIND_ALIAS && + ota->tdescAlias.vt != VT_USERDEFINED) + return obj2var_ti (pInterp, obj, var, ota.m_pti, &(ota->tdescAlias)); TYPEKIND tk = ota->typekind; // the metaclass GUID intfguid = ota->guid; - switch (tk) { case TKIND_ENUM: if (bOk = (Tcl_GetLongFromObj (NULL, obj, &lValue) == TCL_OK)) obj2var(obj, var); - else if (bOk = TypeLib_ResolveConstant (pInterp, obj, ptmp, ota.m_pti)) + else if (bOk = TypeLib_ResolveConstant(pInterp, obj, ptmp, ota.m_pti)) obj2var (ptmp, var); break; case TKIND_DISPATCH: case TKIND_INTERFACE: // both these case require an object with the correct interface - pOptclObj = g_objmap.Find (obj); - if (pOptclObj != NULL) { - ptmpunk = (IUnknown*)(*pOptclObj); - ASSERT (ptmpunk != NULL); - hr = ptmpunk->QueryInterface (intfguid, (void**)&(var.punkVal)); - CHECKHR(hr); - V_VT(&var) = VT_UNKNOWN; + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = NULL; + + // let's first check for the 'special' cases. + + // images: + // check to see if we have tk installed and we're requested a picture + if (g_bTkInit && IsEqualGUID (ota.m_pattr->guid, __uuidof(IPicture))) { + // create picture variant + bOk = obj2picture(pInterp, obj, var); + } else if (((char*)obj)[0] != 0) { + pOptclObj = g_objmap.Find (obj); + if (pOptclObj != NULL) { + ptmpunk = (IUnknown*)(*pOptclObj); + ASSERT (ptmpunk != NULL); + hr = ptmpunk->QueryInterface (intfguid, (void**)&(var.punkVal)); + CHECKHR(hr); + bOk = true; + } else { + ObjectNotFound (pInterp, obj); + } + } else bOk = true; - } else - ObjectNotFound (pInterp, obj); break; case TKIND_COCLASS: - pOptclObj = g_objmap.Find (obj); - if (pOptclObj != NULL) { - var.punkVal = (IUnknown*)(*pOptclObj); - var.punkVal->AddRef(); - V_VT(&var) = VT_UNKNOWN; - bOk = true; + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = NULL; + + if (obj.isnotnull() && ((char*)obj)[0] != 0) { + pOptclObj = g_objmap.Find (obj); + if (pOptclObj != NULL) { + var.punkVal = (IUnknown*)(*pOptclObj); + var.punkVal->AddRef(); + + bOk = true; + } else + ObjectNotFound (pInterp, obj); } else - ObjectNotFound (pInterp, obj); + bOk = true; break; - case TKIND_ALIAS: ASSERT (FALSE); // should be hanlded above. break; // can't handle these types case TKIND_MODULE: + break; + case TKIND_ALIAS: case TKIND_RECORD: + return obj2record(pInterp, obj, var, ota.m_pti); + break; case TKIND_UNION: obj2var (obj, var); bOk = true; @@ -646,13 +802,196 @@ bool obj2var_ti (Tcl_Interp *pInterp, TObjPtr &obj, VARIANT &var, } } + else if (pdesc->vt == VT_PTR) { + ASSERT (pdesc->lptdesc != NULL); + if (pdesc->lptdesc->vt == VT_USERDEFINED) + return obj2var_ti (pInterp, obj, var, pInfo, pdesc->lptdesc); + + ASSERT (pdesc->lptdesc->vt != VT_USERDEFINED); + return obj2var_vt_byref (pInterp, obj, var, pdesc->lptdesc->vt); + } + + // a simple type + else { + ASSERT (pdesc->vt != VT_ARRAY && pdesc->vt != VT_PTR && pdesc->vt != VT_USERDEFINED); + return obj2var_vt (pInterp, obj, var, pdesc->vt); + } + + // arrays - should be easy to do - not enough time right now... + + return bOk; } +/* + *------------------------------------------------------------------------- + * TypeInfoResolveAliasing + * Resolves a type info to its base referenced type. + * + * Result: + * true iff successful. + * + * Side Effects: + * The pointer referenced by pti is updated to point to the base type. + *------------------------------------------------------------------------- + */ +bool TypeInfoResolveAliasing (Tcl_Interp *pInterp, ITypeInfo * pti, ITypeInfo ** presolved) { + ASSERT (pInterp != NULL && pti != NULL && presolved != NULL); + + bool result = false; + CComPtr currentinfo = pti, temp; + OptclTypeAttr pta; + try { + pta = currentinfo; + while (pta->typekind == TKIND_ALIAS && pta->tdescAlias.vt == VT_USERDEFINED) { + CHECKHR(currentinfo->GetRefTypeInfo (pta->tdescAlias.hreftype, &temp)); + currentinfo = temp; + temp.Release(); + pta = currentinfo; + g_libs.EnsureCached (currentinfo); + } + + CHECKHR(currentinfo.CopyTo(presolved)); + result = true; + } catch (char *er) { + Tcl_SetResult (pInterp, er, TCL_VOLATILE); + } catch (HRESULT hr) { + Tcl_SetResult (pInterp, HRESULT2Str(hr), TCL_DYNAMIC); + } catch (...) { + Tcl_SetResult (pInterp, "unknown error in obj2record", TCL_STATIC); + } + return result; +} + +/* + *------------------------------------------------------------------------- + * obj2record + * + * Result: + * + * Side Effects: + * + *------------------------------------------------------------------------- + */ +bool obj2record (Tcl_Interp *pInterp, TObjPtr &obj, PVOID precord, ITypeInfo *pinf) +{ + USES_CONVERSION; + HRESULT hr; + try{ + CComPtr prinf; + CHECKHR(GetRecordInfoFromTypeInfo2(pinf, &prinf)); + + CComPtr pcmp; + CHECKHR(pinf->GetTypeComp (&pcmp)); + + int length = obj.llength (); + if ((length % 2) != 0) + throw ("record definition must have name value pairs"); + + // iterate over the list of name value pairs + for (int i = 0; (i+1) < length; i += 2) { + OptclBindPtr obp; + + char * name = obj.lindex (i); + LPOLESTR lpoleName = A2OLE(name); + TObjPtr ptr = obj.lindex (i+1); + CComVariant vValue; + + // retrieve the vardesc for this item: + hr = pcmp->Bind (lpoleName, 0, INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF | INVOKE_PROPERTYGET, &obp.m_pti, &obp.m_dk, &obp.m_bp); + if (obp.m_dk == DESCKIND_NONE) { + Tcl_SetResult (pInterp, "record doesn't have member called: ", TCL_STATIC); + Tcl_AppendResult (pInterp, name, NULL); + return false; + } + CHECKHR(hr); + + ASSERT (obp.m_dk == DESCKIND_VARDESC); + + if (obp.m_bp.lpvardesc->elemdescVar.tdesc.vt == VT_USERDEFINED) { + CComPtr inforef, inforesolved; + CHECKHR(pinf->GetRefTypeInfo (obp.m_bp.lpvardesc->elemdescVar.tdesc.hreftype, &inforef)); + if (!TypeInfoResolveAliasing (pInterp, inforef, &inforesolved)) + return false; + OptclTypeAttr pta; + pta = inforesolved; + if (pta->typekind == TKIND_RECORD) { + VARIANT var; + CComPtr peti; + VariantInit(&var); + PVOID pfield = NULL; + HRESULT hr = prinf->GetFieldNoCopy (precord, lpoleName, &var, &pfield); + ASSERT (var.vt & VT_RECORD); + CHECKHR(var.pRecInfo->GetTypeInfo (&peti)); + if (!obj2record (pInterp, ptr, var.pvRecord, peti)) + return false; + } else { + if (!obj2var_ti(pInterp, ptr, vValue, obp.m_pti, &(obp.m_bp.lpvardesc->elemdescVar.tdesc))) + return false; + CHECKHR(prinf->PutField (INVOKE_PROPERTYPUT, precord, lpoleName, &vValue)); + } + } else { + if (!obj2var_ti(pInterp, ptr, vValue, obp.m_pti, &(obp.m_bp.lpvardesc->elemdescVar.tdesc))) + return false; + CHECKHR(prinf->PutField (INVOKE_PROPERTYPUT, precord, lpoleName, &vValue)); + } + } + return true; + } catch (char *er) { + Tcl_SetResult (pInterp, er, TCL_VOLATILE); + } catch (HRESULT hr) { + Tcl_SetResult (pInterp, HRESULT2Str(hr), TCL_DYNAMIC); + } catch (...) { + Tcl_SetResult (pInterp, "unknown error in obj2record", TCL_STATIC); + } + return false; +} +/* + *------------------------------------------------------------------------- + * obj2record + * Converts a Tcl object to a record structure declared by a provided + * type info. + * Result: + * true iff successful. + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +bool obj2record (Tcl_Interp *pInterp, TObjPtr& obj, VARIANT&var, ITypeInfo *pinf) { + ASSERT(pInterp != NULL && pinf != NULL); + USES_CONVERSION; + CComBSTR name; + try { + CComPtr precinfo; + OptclTypeAttr pta; + pta = pinf; + //ASSERT (pta->typekind == TKIND_RECORD); + + CComPtr prinf; + CHECKHR(GetRecordInfoFromTypeInfo2(pinf, &prinf)); + + CComPtr pcmp; + CHECKHR(pinf->GetTypeComp (&pcmp)); + + VariantClear(&var); + var.pvRecord = prinf->RecordCreate (); + var.vt = VT_RECORD; + CHECKHR(prinf.CopyTo (&(var.pRecInfo))); + prinf->RecordInit (var.pvRecord); + return obj2record (pInterp, obj, var.pvRecord, pinf); + } catch (char *er) { + Tcl_SetResult (pInterp, er, TCL_VOLATILE); + } catch (HRESULT hr) { + Tcl_SetResult (pInterp, HRESULT2Str(hr), TCL_DYNAMIC); + } catch (...) { + Tcl_SetResult (pInterp, "unknown error in obj2record", TCL_STATIC); + } + return false; +} /* @@ -853,15 +1192,22 @@ bool obj2var_vt (Tcl_Interp *pInterp, TObjPtr &obj, VARIANT &var, VARTYPE vt) IUnknown * ptmpunk = NULL; bool bOk = true; HRESULT hr; + OptclObj *pObj = g_objmap.Find (obj); + if (pObj != NULL) { + if (pObj->m_pta->typekind == TKIND_DISPATCH || (pObj->m_pta->wTypeFlags & TYPEFLAG_FDUAL)) + vt = VT_DISPATCH; + else + vt = VT_UNKNOWN; + } switch (vt) { case VT_DISPATCH: case VT_UNKNOWN: V_VT(&var) = vt; - if (obj.isnull()) - var.punkVal = NULL; - else { + V_UNKNOWN(&var) = NULL; + + if (obj.isnotnull() && ( ((char*)obj)[0] != 0) ) { // attempt to cast from an optcl object pOptclObj = g_objmap.Find (obj); @@ -995,7 +1341,7 @@ bool SplitBrackets (Tcl_Interp *pInterp, Tcl_Obj *pObj, if (Tcl_EvalObj (pInterp, pcmd) == TCL_ERROR) return false; - CONST84 char * okstr = Tcl_GetStringResult (pInterp); + const char * okstr = Tcl_GetStringResult (pInterp); if (okstr[0] == '0') { Tcl_SetResult (pInterp, "property format is incorrect: ", TCL_STATIC); Tcl_AppendResult (pInterp, Tcl_GetStringFromObj(pObj, NULL), NULL); @@ -1015,6 +1361,25 @@ bool SplitBrackets (Tcl_Interp *pInterp, Tcl_Obj *pObj, return true; } + + +/* + *------------------------------------------------------------------------- + * obj2picture + * Convert the name of a tk image to an com object supporting IPicture + * + * Result: + * True iff successful. Else, error description in Tcl interpreter. + * + * Side Effects: + * None. + *------------------------------------------------------------------------- + */ +bool obj2picture(Tcl_Interp *pInterp, TObjPtr &obj, VARIANT &var) { + return false; // NOT IMPLEMENTED YET +} + + /// Tests TCL_CMDEF (Obj2VarTest) { diff --git a/src/utility.h b/src/utility.h index 510c692..6ad1903 100644 --- a/src/utility.h +++ b/src/utility.h @@ -43,11 +43,21 @@ void OptclTrace(LPCTSTR lpszFormat, ...); #else # define TRACE -#endif +#endif // _DEBUG + +// TRACE_OPTCLOBJ +// Gives a trace output for an optcl object, in terms of its name, current interface, and reference count +#ifdef _DEBUG +# define TRACE_OPTCLOBJ(obj) {TObjPtr interfacename; obj->InterfaceName(interfacename); OptclTrace("%s %s --> %d\n", (char*)interfacename, obj->m_name.c_str(), obj->m_refcount);} +#else +# define TRACE_OPTCLOBJ +#endif // _DEBUG + + #define TCL_CMDEF(fname) int fname (ClientData cd, Tcl_Interp *pInterp, int objc, Tcl_Obj *CONST objv[]) #define CHECKHR(hr) if (FAILED(hr)) throw(hr) -#define CHECKHR_TCL(hr, i, v) if (FAILED(hr)) {Tcl_SetResult (i, HRESULT2Str(hr), TCL_DYNAMIC); return v;} +#define CHECKHR_TCL(hr, i, v) {HRESULT _hr = (hr); if (FAILED(_hr)) {Tcl_SetResult (i, HRESULT2Str(_hr), TCL_DYNAMIC); return v;}} #define SETDISPPARAMS(dp, numArgs, pvArgs, numNamed, pNamed) \ {\ @@ -62,6 +72,33 @@ void OptclTrace(LPCTSTR lpszFormat, ...); #define _countof(x) (sizeof(x)/sizeof(x[0])) +template +class auto_array { +public: + typedef T* TPTR; + auto_array () : m_ptr(NULL) {} + auto_array(unsigned long items) : m_ptr(NULL) { + Allocate(items); + } + ~auto_array() { ReleaseArray();} + void ReleaseArray () { + if (m_ptr != NULL) { + delete [] m_ptr; + m_ptr = NULL; + } + } + TPTR Allocate(unsigned long items) { + ReleaseArray(); + m_ptr = new T[items]; + return m_ptr; + } + operator TPTR () { + return m_ptr; + } +protected: + TPTR m_ptr; +}; + template void delete_ptr (T* &ptr) { if (ptr != NULL) { @@ -83,11 +120,15 @@ template T* delete_array (T *&ptr) { class OptclObj; -bool var2obj (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &presult, OptclObj **ppObj = NULL); +bool var2obj (Tcl_Interp *pInterp, VARIANT &var, ITypeInfo *pti, TObjPtr &presult, OptclObj **ppObj = NULL); bool obj2var_ti (Tcl_Interp *pInterp, TObjPtr &obj, VARIANT &var, ITypeInfo *pInfo, TYPEDESC *pdesc); bool obj2var_vt (Tcl_Interp *pInterp, TObjPtr &obj, VARIANT &var, VARTYPE vt); bool obj2var_vt_byref (Tcl_Interp *pInterp, TObjPtr &obj, VARIANT &var, VARTYPE vt); void obj2var (TObjPtr &obj, VARIANT &var); +bool record2obj (Tcl_Interp *pInterp, VARIANT &var, TObjPtr &result); +bool obj2record (Tcl_Interp *pInterp, TObjPtr &obj, VARIANT &var, ITypeInfo *pinf); +bool obj2picture(Tcl_Interp *pInterp, TObjPtr &obj, VARIANT &var); + void OptclVariantClear (VARIANT *pvar); @@ -103,8 +144,8 @@ int ObjectNotFound (Tcl_Interp *pInterp, const char *name); void SplitTypedString (char *pstr, char ** ppsecond); bool SplitObject (Tcl_Interp *pInterp, Tcl_Obj *pObj, const char * tokens, Tcl_Obj **ppResult); -bool SplitBrackets (Tcl_Interp *pInterp, Tcl_Obj *pObj, - TObjPtr & result); +bool SplitBrackets (Tcl_Interp *pInterp, Tcl_Obj *pObj, TObjPtr & result); +bool TypeInfoResolveAliasing (Tcl_Interp *pInterp, ITypeInfo * pti, ITypeInfo ** presolved); /// TESTS TCL_CMDEF (Obj2VarTest); diff --git a/temp code/reg.tcl b/temp code/reg.tcl new file mode 100644 index 0000000..09ade01 --- /dev/null +++ b/temp code/reg.tcl @@ -0,0 +1,139 @@ +package require registry + + +namespace eval COM { + + # categories + # retrieve a list of all category names + proc categories {} { + set alldata {} + set k "HKEY_CLASSES_ROOT\\Component Categories" + set cats [registry keys $k] + + foreach cat $cats { + set values [registry values $k\\$cat] + set data {} + foreach value $values { + lappend data [registry get $k\\$cat $value] + } + lappend alldata $data + } + + return $alldata + } + + + # collate all the category names under the category clsid (parameter 1) into an + # array passed by name + proc collate_category_names {category arrname} { + upvar $arrname categories + + set ck "HKEY_CLASSES_ROOT\\Component Categories\\$category" + catch { + foreach value [registry values $ck] { + catch {set categories([registry get $ck $value]) ""} + } + } err + return $err + } + + + # collates all categories for a given clsid in an array that is passed by name + proc clsid_categories_to_array {clsid arrname} { + upvar $arrname categories + set k "HKEY_CLASSES_ROOT\\CLSID\\$clsid" + + # catch if there aren't any implemented categories + + foreach subkey [registry keys "HKEY_CLASSES_ROOT\\CLSID\\$clsid"] { + switch $subkey { + {Implemented Categories} { + foreach category [registry keys "$k\\$subkey"] { + collate_category_names $category categories + } + } + + Programmable { + array set categories {{Automation Objects} {}} + } + + Control { + array set categories {Controls {}} + } + + DocObject { + array set categories {{Document Objects} {}} + } + + Insertable { + array set categories {{Embeddable Objects} {}} + } + } + } + + } + + # retrieves, as a list, the categories for the given clsid + proc clsid_categories {clsid} { + array set categories {} + clsid_categories_to_array $clsid categories + return [array names categories] + } + + + # retrieves all clsids that match the category name given by the first parameter + proc clsids {{cat {}}} { + array set categories {} + set clsidk "HKEY_CLASSES_ROOT\\CLSID" + if {$cat == {}} { + return [registry keys $clsidk] + } + + # else ... + + set classes {} + + foreach clsid [registry keys $clsidk] { + catch [unset categories] + array set categories {} + clsid_categories_to_array $clsid categories + if {[array names categories $cat]!={}} { + lappend classes $clsid + } + } + return $classes + } + + + + # provides a description for the clsid + proc describe_clsid {clsid} { + set clsidk "HKEY_CLASSES_ROOT\\CLSID\\$clsid" + return [registry get $clsidk {}] + } + + + + # retrieves a list of clsid descriptor for all clsids that have the category specified by + # parameter one. If parameter is {} then all clsids are returned. + proc descrive_all_clsids {{cat {}}} { + set l {} + foreach clsid [categories::all_clsids $cat] { + lappend l [categories::describe_clsid $clsid] + } + return [lsort -dictionary $l] + } + + # retrieve the programmatics identifier for a clsid. + # If any exist, the result of this procedure is the programmatic identifier for the + # the clsid, followed by an optional version independent identifier + proc progid_from_clsid {clsid} { + set clsidk "HKEY_CLASSES_ROOT\\CLSID\\$clsid" + set progid {} + set verindid {} + catch {set progid [registry get "$clsidk\\ProgID" {}]} + catch {lappend progid [registry get "$clsidk\\VersionIndependentProgID" {}]} + return $progid + } +} + diff --git a/tests/calendar.tcl b/tests/calendar.tcl index 2ee371b..6543fa4 100644 --- a/tests/calendar.tcl +++ b/tests/calendar.tcl @@ -39,11 +39,11 @@ pack .cal # bind to the calendar AfterUpdate event # routing it to the tcl procedure onupdate # -optcl::bind $cal AfterUpdate onupdate +#optcl::bind $cal AfterUpdate onupdate # get the current value -set currentdate [$cal : value] +#set currentdate [$cal : value] # make a button to view the type information of diff --git a/tests/pdf.tcl b/tests/pdf.tcl index 9304dca..bf11079 100644 --- a/tests/pdf.tcl +++ b/tests/pdf.tcl @@ -4,7 +4,7 @@ package require optcl bind . {console show} wm title . {PDF Document in Tk} -set pdf [optcl::new -window .pdf {d:/program files/adobe/acrobat3/acrobat.pdf}] +set pdf [optcl::new -window .pdf {C:\Program Files\Adobe\Acrobat 4.0\Help\ENU\acrobat.pdf}] .pdf config -width 500 -height 300 pack .pdf -fill both -expand 1