Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

C++/TCL Need Solution to Compile Error c2784

314 views
Skip to first unread message

JesusCh...@gmail.com

unread,
Apr 12, 2007, 3:24:04 AM4/12/07
to
I have a vendor (INTEG PG) that provides an SDK that includes
a .dll, .h and .lib file. Since I haven't program in C for over ten
years, I looked for a solution to repackage the DLL to be TCL
friendly. I was told to use CPPTCL. After reading their
documentation, it was ideal.

I will try to include all my notes. It may be an overkill, but I want
to share all that I have, so someone can replicate my results if they
decide to try it out.

The following is a list of SW used.


jnior 300 SDK - Provided by Integ PG with the CD that came with the
HW

TCL/TK - Downloaded from Activestate.com (version
ActiveTcl8.4.14.0.272572-win32-ix86-threaded.exe)

BOOST - Downloaded from http://www.boost.org/ (version
boost_1_33_1.zip)

CPPTCL - Downloaded from http://cpptcl.sourceforge.net/ (version
cpptcl-1.1.3.zip)

Microsoft Visual C++ Toolkit 2005 - Downloaded from
http://msdn.microsoft.com/vstudio/express/visualc/default.aspx

Note that CPPTCL requires this version of C++ Compilier because MS
Visual Studio 6 will not compile it.
For more information see http://cpptcl.sourceforge.net/ and here
http://wiki.tcl.tk/13040

How to install all the packages

I installed ActviceTCL C:\Program Files\Tcl and not C:\Tcl

I then created the following directory C:\Program Files\TCL_TK

I unzipped boost_1_33_1.zip in the C:\Program Files\TCL_TK directory I
unzipped cpptcl-1.1.3.zip in the C:\Program Files\TCL_TK directory I
created a sub-directory C:\Program Files\TCL_TK\integpg and copy the
jnior SDK files there which are the files that are in the D:
\jnior310\devel\Applications SDK\SDK Files\Windows DLL\dll


I Used the example from http://cpptcl.sourceforge.net/doc/quickstart.html
to test things out


I named the example file "hiworld.cpp" which I placed C:\Program Files
\TCL_TK\cpptcl-1.1.3

To compile I needed to do one step to set up environmental variables

Open a DOS Prompt

dosPrompt> cd C:\Program Files\Microsoft Visual Studio 8\Common7\Tools

source env file
dosPrompt> vsvars32.bat

verify env
dosPrompt> echo %PATH%

To Compile the hello world program, I did the following.
dosPrompt> cd C:\Program Files\TCL_TK\cpptcl-1.1.3

compile command
dosPrompt> "C:/Program Files/Microsoft Visual Studio 8/VC/bin/cl.exe"
hiworld.cpp cpptcl.cpp /EHs /I "C:/Program
Files/Tcl/include" /I "C:/Program Files/TCL_TK/boost_1_33_1" /I "C:/
Program Files/TCL_TK/cpptcl-1.1.3" /LD
"C:/Program Files/Tcl/lib/Tcl84.lib"

To test it out

dosPrompt> tclsh
tclPrompt> load hiworld.dll
tclPrompt> hi
Hello C++/Tcl!
It works!
tclPrompt> + 2 5
7
tclPrompt> exit
dosPrompt>


Before i attempt to create the TCL friendly DLL., I build a simple
program (using the SDK example) to make
sure I could compile using the jnior 310 SDK. Which worked! This test
did not use CPPTCL.

Now that I know things worked, I went ahead to build the TCL friendly
DLL for the jnior 310.

I also placed the file in C:\Program Files\TCL_TK\cpptcl-1.1.3 called
jnior310tcl.cpp
I took all the prototypes from the header file to build the calls,
needed within the CPPTCL_MODULE function

Example: jnior_300.h has the following prototypes

long __stdcall Connect(char *ip, unsigned short port, long *handle,
char *user_name, char *password);

which I used to create the following line

i.def("Connect", Connect);


To compile I use the following command and redirected the output
because of the amount of errors generated.

dosPrompt> "C:/Program Files/Microsoft Visual Studio 8/VC/bin/cl.exe"
jnior310tcl.cpp cpptcl.cpp /EHs /I
"C:/Program Files/Tcl/include" /I "C:/Program Files/TCL_TK/
boost_1_33_1" /I "C:/Program Files/TCL_TK/cpptcl-1.1.3"
/LD "C:/Program Files/Tcl/lib/Tcl84.lib" /LD "C:/Program Files/TCL_TK/
integpg/jnior300.lib" > error.txt


CODE FOR "hiworld.cpp"
==============================START
// Compile command
// "C:/Program Files/Microsoft Visual Studio 8/VC/bin/cl.exe"
hiworld.cpp cpptcl.cpp /EHs /I "C:/Program Files/Tcl/include" /I "C:/
Program Files/TCL_TK/boost_1_33_1" /I "C:/Program Files/TCL_TK/
cpptcl-1.1.3" /LD "C:/Program Files/Tcl/lib/Tcl84.lib" > error4.txt

#include "cpptcl.h"
#include <iostream>

#define _SCL_SECURE_NO_DEPRECATE

using namespace std;

extern "C"
{
__declspec(dllexport) int Hiworld_Init(Tcl_Interp* interp);
}

void hello() {
cout << "Hello C++/Tcl!\nIt works!" << endl;
}

int sum(int a, int b) {
return a + b;
}

CPPTCL_MODULE(Hiworld, i) {
i.def("hi", hello);
i.def("add",sum);
i.def("+",sum);
}

==============================STOP


CODE FOR "jnior310tcl2.cpp"
==============================START
//
// Compile command
// cd C:/Program Files/TCL_TK/cpptcl-1.1.3
// "C:/Program Files/Microsoft Visual Studio 8/VC/bin/cl.exe"
jnior310tcl2.cpp cpptcl.cpp /EHs /I "C:/Program Files/Tcl/include" /I
"C:/Program Files/TCL_TK/boost_1_33_1" /I "C:/Program Files/TCL_TK/
cpptcl-1.1.3" /I "C:/Program Files/TCL_TK/integpg" /LD "C:/Program
Files/Tcl/lib/Tcl84.lib" /LD "C:/Program Files/TCL_TK/integpg/
jnior300.lib" > error3.txt
//

#include "cpptcl.h"
#include "jnior_300.h"
#include <iostream>

using namespace std;

#define _SCL_SECURE_NO_DEPRECATE

extern "C"
{
__declspec(dllexport) int Jnior300tcl_Init(Tcl_Interp* interp);
}

CPPTCL_MODULE(Jnior300tcl, i) {

// i.def("GetDLLVersion", GetDLLVersion);

// Socket Connection
// i.def("queryForPort", queryForPort);
i.def("Connect", Connect);
// i.def("Disconnect", Disconnect);
// i.def("GetUserID", GetUserID);
// i.def("SetRetryCount", SetRetryCount);

}

==============================STOP


ERROR MESSAGE
==============================START
jnior310tcl2.cpp
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4,T5,T6,T7,T8,T9),const
Tcl::policies &)' : could not deduce template argument for 'overloaded
function type' from 'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(570) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4,T5,T6,T7,T8,T9),const
Tcl::policies &)' : could not deduce template argument for 'overloaded
function type' from 'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(570) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4,T5,T6,T7,T8),const
Tcl::policies &)' : could not deduce template argument for 'overloaded
function type' from 'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(558) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4,T5,T6,T7,T8),const
Tcl::policies &)' : could not deduce template argument for 'overloaded
function type' from 'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(558) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4,T5,T6,T7),const Tcl::policies
&)' : could not deduce template argument for 'overloaded function
type' from 'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(547) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4,T5,T6,T7),const Tcl::policies
&)' : could not deduce template argument for 'overloaded function
type' from 'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(547) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4,T5,T6),const Tcl::policies
&)' : could not deduce template argument for 'overloaded function
type' from 'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(537) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4,T5,T6),const Tcl::policies
&)' : could not deduce template argument for 'overloaded function
type' from 'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(537) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4,T5),const Tcl::policies &)' :
could not deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(527) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4,T5),const Tcl::policies &)' :
could not deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(527) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4),const Tcl::policies &)' :
could not deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(517) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3,T4),const Tcl::policies &)' :
could not deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(517) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3),const Tcl::policies &)' : could
not deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(508) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2,T3),const Tcl::policies &)' : could
not deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(508) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2),const Tcl::policies &)' : could not
deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(499) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1,T2),const Tcl::policies &)' : could not
deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(499) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1),const Tcl::policies &)' : could not
deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(490) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(T1),const Tcl::policies &)' : could not
deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(490) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(void),const Tcl::policies &)' : could not
deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(481) : see
declaration of 'Tcl::interpreter::def'
jnior310tcl2.cpp(28) : error C2784: 'void Tcl::interpreter::def(const
std::string &,R (__cdecl *)(void),const Tcl::policies &)' : could not
deduce template argument for 'overloaded function type' from
'overloaded function type'
c:\program files\tcl_tk\cpptcl-1.1.3\cpptcl.h(481) : see
declaration of 'Tcl::interpreter::def'
cpptcl.cpp
Generating Code...
==============================STOP


As you can see the only error I'm getting is this C2784 with the
message:
"could not deduce template argument for 'overloaded function type'
from 'overloaded function type'"

Any help is greatly appreciated.

-Jesus

Helmut Giese

unread,
Apr 12, 2007, 5:16:23 AM4/12/07
to
On 12 Apr 2007 00:24:04 -0700, JesusCh...@gmail.com wrote:
>As you can see the only error I'm getting is this C2784 with the
>message:
>"could not deduce template argument for 'overloaded function type'
>from 'overloaded function type'"
Hi Jesus,
1) This is evidently a C++ error (the concepts of 'template' and
'overloaded function' do not exist in C).
2) You have a very demanding environment. In case you don't know it:
The BOOST library is one of the most advanced libs in the C++ world
with respect to C++ features they use - it is so demanding that the
ability to compile (certain parts or all of) this library is
considered a mark of quality for the compiler in question. If ever
someone faces a compiler problem which is related to some feature of
BOOST (which may or may not be the case here) he'd better have a true
C++ expert at his side.
3) Hm, you haven't been programming in C for a while - the error comes
from C++ - it could well be that you are fighting out of your weight
here.

In all probability you don't need C++ at all: Look into the header
file to see what the DLL exports:
- Just functions and/or variables? You will be fine with a plain C
interface.
- Classes? Ok, then you're into C++ world - but this is maybe not the
best news group for this kind of question.

Assuming that C will suffice: Start by writing a Tcl interface for
just one or two functions of your DLL - something which when called
will yield a noticable result. People here could probably help you
with that.
Or have a look at SWIG - for a plain C interface it should be able to
automate a lot of this work (I am told - I have not used it, being the
kind of C programmer who prefers to know what he's doing :) ).
Good luck
Helmut Giese

David Gravereaux

unread,
Apr 12, 2007, 12:55:54 PM4/12/07
to
JesusCh...@gmail.com wrote:

> Any help is greatly appreciated.

Hi Jesus,

I can't hold back.. I must say my real thoughts..

Automation sux!

Be it CPPTCL or even SWIG, your interfaces never turn-out the way you really want
it. Don't put me in a box, please. Forget the c++ template error thing for the
moment.

Last weekend I wrote an interface library for a PC-controlled USB oscilloscope.
It has a whooping 82 function calls and took me 2 days to get it 70% done. But
the work was well worth it as I can specify all parameters for exactly how I want
the interface to be.

You do need to know how Tcl works on the inside but the learning curve isn't all
that steep. Once you have an extension framework, adding more commands for the
externals isn't that hard, and more than likely all the functions are somewhat
orthogonal so you end-up reusing error checking and param decoding code.

Spend the time to learn Tcl's C API and hack away at your extension without
fighting those helper tools. Just ask questions here, if you get a bit stuck on
some concepts like Tcl_Obj reference counting, or whatever.


Here's an example of one of the calls from the scope library I turned into a Tcl
command..

word SetSensitivity(byte byCh, double *dSens); /* from scope control dll */

becomes:

Tcl_ObjCmdProc SetSensitivityCmd;

__declspec(dllexport) int
Tpscope_Init (Tcl_Interp *interp)
{
#ifdef USE_TCL_STUBS
if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
return TCL_ERROR;
}
#endif
....
# define MAKECMD(name) \
Tcl_CreateObjCommand(interp, STRINGIFY(JOIN(tiepie::,name)), \
JOIN(name,Cmd), 0L, 0L);
....
MAKECMD(SetSensitivity);
....
}

int
SetSensitivityCmd (ClientData clientData, Tcl_Interp *interp,
int objc, struct Tcl_Obj * CONST * objv)
{
word ok;
byte ch;
double sensitivity;

if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "channel sensitivity");
return TCL_ERROR;
}

if (GetChannelFromObj(interp, objv[1], &ch) != TCL_OK) {
return TCL_ERROR;
}

if (Tcl_GetDoubleFromObj(interp, objv[2], &sensitivity) != TCL_OK) {
return TCL_ERROR;
}

ok = SetSensitivity(ch, &sensitivity);
if (TiePieResultOK(interp, ok)) {
/* return actual */
Tcl_SetObjResult(interp, Tcl_NewDoubleObj(sensitivity));
return TCL_OK;
}
return TCL_ERROR;
}

byte
GetChannelFromObj(Tcl_Interp *interp, Tcl_Obj *obj, byte *ch)
{
int channel;

*ch = 0;
if (Tcl_GetIntFromObj(interp, obj, &channel) != TCL_OK) {
return TCL_ERROR;
}

if (channel < 0 || channel > 4) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("channel out-of-range",-1));
return TCL_ERROR;
}

*ch = (byte)channel;
return TCL_OK;
}

int
TiePieResultOK (Tcl_Interp *interp, word result)
{
const char *msg, *eName;

switch (result) {
case E_NO_ERRORS:
return 1;

case E_NO_HARDWARE:
eName = "E_NO_HARDWARE";
msg = "Hardware is not connected";
break;
case E_NOT_INITIALISED:
eName = "E_NOT_INITIALISED";
msg = "Hardware is not initialized";
break;
case E_NOT_SUPPORTED:
eName = "E_NOT_SUPPORTED";
msg = "Feature is not supported in your hardware";
break;
case E_NO_GENERATOR:
eName = "E_NO_GENERATOR";
msg = "Hardware doesn't have an AWG";
break;
case E_INVALID_CHANNEL:
eName = "E_INVALID_CHANNEL";
msg = "Invalid channel";
break;
case E_INVALID_VALUE:
eName = "E_INVALID_VALUE";
msg = "Invalid input value";
break;
case (E_LAST<<1):
eName = "E_NO_SUCH_API";
msg = "API not found in DLL";
break;
default:
eName = "????";
msg = "Unknown error";
break;
}

if (interp) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, -1));
Tcl_SetErrorCode(interp, "TIEPIE", eName, msg, NULL);
}
return 0;
}


--
A voice crackles in Calvin's radio:
"Enemy fighters at two o'clock!"
"Roger. What should I do until then?"

signature.asc

JesusCh...@gmail.com

unread,
Apr 13, 2007, 3:19:02 AM4/13/07
to
On Apr 12, 2:16 am, Helmut Giese <hgi...@ratiosoft.com> wrote:
> On 12 Apr 2007 00:24:04 -0700, JesusChuyCam...@gmail.com wrote:>As you can see the only error I'm getting is this C2784 with the

Hi Helmut,

First of all, thank you for you time. After I looked more carefully,
the errors do seem to come from BOOST rather than CPPTCL. All the
newsgroups I've searched, also mention that the compiler is broken.
I've looked at the jnior_300h header file and it contains the
following:
58 long __stdcall SomeFunctionName(param.......)
7 #define ..............
6 typedef void (CALLBACK* .......)(....................)
3 struct ..............................

Which my guess is that it is all C. Though the documents state to use
C++ to compile. I'll take a look at SWIG to see if that helps at all,
or makes things easier. Funny things is that for the last 5 years
I've been writing all my code in TCL (and at time is TK) in a UNIX
environment. This is the first time I had to use the C capabilities
for TCL and for MS Windows. I'll let you know what happens.

JesusCh...@gmail.com

unread,
Apr 13, 2007, 3:54:43 AM4/13/07
to
On Apr 12, 9:55 am, David Gravereaux <davyg...@pobox.com> wrote:
> signature.asc
> 1KDownload

hi David,

Like Hemut, I just want to thank you for your time. Wow 82 functions
that have to TCL friendly. Looking at your example and others I found
on the net, have scared me to take that approach, which is why BOOST/
CPPTCL and now even SWIG seem promising. I was given three weeks to
complete this task. The first week was choosing which Language I would
build the project on (TCL was selected) and since I needed a compiler
to create a TCL friendly DLL it took the rest of that week, due to non
Admin permissions on my PC. After trying to get the vendors example
code to compile, I spent most of the second week trying to get help
from the vendor. Doesn't help when they are three hours ahead and I
work a late 2nd shift. The vendor did fix their SDK, but couldn't help
me with CPPTCL since they are not familiar with it. After looking for
answers, I decided to post a question. It always sucks when the
simple "hello world" program works, but the real thing breaks. I' will
be looking into SWIG, because just like CPPTCL, if the SDK changes
rebuilding the DLL will be a breeze. But if worse comes to worse, I
guess I will have to learn the TCL C API like you mention and get it
done.

Christian Gollwitzer

unread,
Apr 13, 2007, 4:13:31 AM4/13/07
to
JesusCh...@gmail.com wrote:
> It always sucks when the
> simple "hello world" program works, but the real thing breaks. I' will
> be looking into SWIG, because just like CPPTCL, if the SDK changes
> rebuilding the DLL will be a breeze. But if worse comes to worse, I
> guess I will have to learn the TCL C API like you mention and get it
> done.

SWIG does its job very well. For a first approximation, you can probably
just pass the headerfile into it like

swig -tcl8 -c++ headerfile.h


If you want a crisp tclish interface, you can improve the interface by
writing proxy classes. Also exotic types can be integrated quite easy by
providing a so called "typemap". With the typemap you can change the way
SWIG interfaces TCL and C for specific function arguments. So you have
the full control, but don't need to repeat tedious work. The only
disadvantage may be some performance lost in comparison with a fully
handwritten glue code.

Christian

JesusCh...@gmail.com

unread,
Apr 13, 2007, 5:07:59 AM4/13/07
to
On Apr 13, 1:13 am, Christian Gollwitzer <Christian.Gollwit...@uni-
bayreuth.de> wrote:

Hi Christian,

Thank you, for your time.

I tried your example, and got an error, "no module name" therefore I
added:
%module jnior310tcl_swig

and when I tried the command, I got the same error I've been having
this past hour. It has to do with the use of "__stdcall" issue.

Here is my example:

Here is the first 12 lines "jnior_300.h"
===================================START
#pragma once

#ifndef CALLBACK
#define CALLBACK __stdcall
#endif

int GetDLLVersion (char *version);

// Socket Connection
long __stdcall queryForPort(char *ip, char *user_name, char
*password);


long __stdcall Connect(char *ip, unsigned short port, long *handle,
char *user_name, char *password);

long __stdcall Disconnect(long handle);
===================================STOP

Note that the rest of the file contains 55 more function prototypes
that are "long __stadcall functionName(arg1, arg2,....)"

Here is how my SWIG Interface file looks like based on the SWIG
information for MS Windows.
===================================START
// jnior310tcl_swig.i : A Simple SWIG interface for Jnior 310 Module
//
// SWIG Command
// cd "C:/Program Files/TCL_TK/integpg"
// "C:/Program Files/TCL_TK/swigwin-1.3.31/swig.exe" -tcl8 -c++
jnior310tcl_swig.i
//
// Compile Command
// cd "C:/Program Files/TCL_TK/integpg"


// "C:/Program Files/Microsoft Visual Studio 8/VC/bin/cl.exe"

jnior310tcl_swig_wrap.c /EHs /I "C:/Program Files/Tcl/include" /I "C:/
Program Files/TCL_TK/integpg" /LD "C:/Program Files/Tcl/lib/
Tcl84.lib" /LD "C:/Program Files/TCL_TK/integpg/jnior300.lib" >
error7.txt

%module jnior310tcl_swig
%{
#include "jnior_300.h"
__declspec(dllexport) long __stdcall queryForPort(char, char, char);
__declspec(dllexport) long __stdcall Connect(char, unsigned short,
long, char, char);
__declspec(dllexport) long __stdcall Disconnect(long);
%}

// ANSI C/C++ prototypes
long __stdcall queryForPort(char *ip, char *user_name, char
*password);


long __stdcall Connect(char *ip, unsigned short port, long *handle,
char *user_name, char *password);

long __stdcall Disconnect(long handle);
===================================STOP

I always get an error on line 20 which is "long __stdcall
queryForPort.....". I tried varies things, like removing the ";" but
I still get the same syntax error. If you can point out what I'm
doing wrong, I would greatly appreciate that.

Note that the vendor only provided me with three files the header file
jnior_300.h, the dll file jnior300.dll and the library file
jnior300.lib.

-Jesus


Christian Gollwitzer

unread,
Apr 13, 2007, 6:15:18 AM4/13/07
to
JesusCh...@gmail.com wrote:
> %module jnior310tcl_swig
> %{
> #include "jnior_300.h"
> __declspec(dllexport) long __stdcall queryForPort(char, char, char);
> __declspec(dllexport) long __stdcall Connect(char, unsigned short,
> long, char, char);
> __declspec(dllexport) long __stdcall Disconnect(long);
> %}
>
> // ANSI C/C++ prototypes
> long __stdcall queryForPort(char *ip, char *user_name, char
> *password);
> long __stdcall Connect(char *ip, unsigned short port, long *handle,
> char *user_name, char *password);
> long __stdcall Disconnect(long handle);
> ===================================STOP
>
> I always get an error on line 20 which is "long __stdcall
> queryForPort.....". I tried varies things, like removing the ";" but
> I still get the same syntax error. If you can point out what I'm
> doing wrong, I would greatly appreciate that.

Which error? A SWIG-Error? That will be probably because of stdcall,
just drop it. Maybe you can fiddle with

#ifdef SWIG
#define __stdcall
#endif

or similiar to stuff everything into one headerfile. If you get compile
errors from the C compiler, make sure you can compile a program in pure
C using that SDK.

> Note that the vendor only provided me with three files the header file
> jnior_300.h, the dll file jnior300.dll and the library file
> jnior300.lib.

That's correct, you should not need more.

One hint: The "long* handle" is probably meant to be an output
parameter; in that case, the function will be unusable unless you add
some typemap (see SWIG docs)

Christian

David Gravereaux

unread,
Apr 13, 2007, 4:02:25 PM4/13/07
to
David Gravereaux wrote:

> cl -nologo -IC:\tcl\include jnior_tcl.c -link -dll -out:jniortcl.dll
> -libpath:C:\Tcl\lib jnior300.lib

Add an -MD in there:

cl -nologo -MD -IC:\tcl\include jnior_tcl.c -link -dll -out:jniortcl.dll
-libpath:C:\Tcl\lib jnior300.lib

--
I'm just very selective about the reality I choose to accept.
--- Calvin

signature.asc

David Gravereaux

unread,
Apr 13, 2007, 4:51:42 PM4/13/07
to
Christian Gollwitzer wrote:

> SWIG does its job very well. For a first approximation, you can probably
> just pass the headerfile into it like
>
> swig -tcl8 -c++ headerfile.h

I'll put $100 down that the time it takes Jesus to get the time saving tools to
work, will be greater than the time it takes to hand-roll the glue code.

Don't fear the core! The water is warm... just jump in :)
http://www.tcl.tk/man/tcl8.4/TclLib/contents.htm

--
"I suppose the secret to happiness is learning to appreciate the moment."
-Calvin

signature.asc

Alexandre Ferrieux

unread,
Apr 14, 2007, 2:52:33 PM4/14/07
to
On Apr 13, 10:51 pm, David Gravereaux <davyg...@pobox.com> wrote:
> Christian Gollwitzer wrote:
> > SWIG does its job very well. For a first approximation, you can probably
> > just pass the headerfile into it like
>
> > swig -tcl8 -c++ headerfile.h
>
> I'll put $100 down that the time it takes Jesus to get the time saving tools to
> work, will be greater than the time it takes to hand-roll the glue code.
>

If he's going to spend time on each function to analyse its prototype,
then maybe he could stay at the Tcl level with ffidl (since he said he
was rusty with C). Or could he ? I'm sincerely asking, having never
used ffidl myself. Thanks for any advice.

-Alex

David Gravereaux

unread,
Apr 14, 2007, 5:40:52 PM4/14/07
to

Now you got me wondering, too.

http://elf.org/ffidl/
http://elf.org/pub/ffidl-0.6/Ffidl0.6.zip

Here's a few bits for that API I did last weekend, now done with ffidl just to see
how it works for me.

-- tiepie.tcl --
package require Ffidl
package provide tiepie

namespace eval ::tiepie {
set lib "c:/programming/tpscope/debug/hs3.dll"
}

# types used from tiepie.h
::ffidl::typedef byte uint8
::ffidl::typedef word uint16
::ffidl::typedef dword uint32
::ffidl::typedef integer long

proc ::tiepie::ResultERR {result nameVar infoVar} {
upvar $nameVar name $infoVar info
switch -- $result {
0 {
return 0
}
1 {
set name "E_NO_HARDWARE"
set info "Hardware is not connected"
}
2 {
set name "E_NOT_INITIALISED"
set info "Hardware is not initialized"
}
4 {
set name "E_NOT_SUPPORTED"
set info "Feature is not supported in your hardware"
}
8 {
set name "E_NO_GENERATOR"
set info "Hardware doesn't have an AWG"
}
16 {
set name "E_INVALID_CHANNEL"
set info "Invalid channel"
}
32 {
set name "E_INVALID_VALUE"
set info "Invalid input value"
}
default {
puts "oops"
}
}
return 1
}

# word InitInstrument (word address);
#
::ffidl::callout ::tiepie::_InitInstrument {word} word [::ffidl::symbol
$::tiepie::lib InitInstrument] stdcall

proc ::tiepie::InitInstrument {{address 0}} {
set rtn [_InitInstrument $address]
if {[ResultERR $rtn name info]} {
return -code error -errorinfo $info -errorcode [list TIEPIE $name $info]
}
return
}

# word ExitInstrument (void);
#
::ffidl::callout ::tiepie::_ExitInstrument {void} word [::ffidl::symbol
$::tiepie::lib ExitInstrument] stdcall

proc ::tiepie::ExitInstrument {} {
set rtn [_ExitInstrument]
if {[ResultERR $rtn name info]} {
return -code error -errorinfo $info -errorcode [list TIEPIE $name $info]
}
return
}

# word GetSerialNumber (dword *serial);
#
::ffidl::callout ::tiepie::_GetSerialNumber {pointer-var} word [::ffidl::symbol
$::tiepie::lib GetSerialNumber] stdcall

proc ::tiepie::GetSerialNumber {} {
set serial [binary format [::ffidl::info format word] 0]
set rtn [_GetSerialNumber serial]
if {[ResultERR $rtn name info]} {
return -code error -errorinfo $info -errorcode [list TIEPIE $name $info]
}
binary scan $serial [::ffidl::info format word] endptr
return $serial
}

-- END tiepie.tcl --

It took a bit of work deciphering the documentation, but I think that looks right.
I'm a bit worried about the _GetSerialNumber callout as the function writes the
value to the pointer owned by the integer Tcl_Obj. Is this valid? Doesn't look
valid to me. Not a big deal with pointers to numbers, but strings would make a
mess of things.

Does it work, though? NOPE! I can't get the callout to GetSerialNumber to not
cause Tcl to crash.

Well, In the time it took me to start using ffidl and get this far, I could have
written about 20 fully working on the first try hand-rolled Tcl_ObjCmdProc
functions in C.

ffidl is not for me.

--
If you care, you just get disappointed all the time. If you don't care
nothing matters so you are never upset. -- Calvin

signature.asc

David Gravereaux

unread,
Apr 14, 2007, 7:24:46 PM4/14/07
to
Ok, I figure-out what I was doing wrong.

proc ::tiepie::GetSerialNumber {} {
set serial [binary format [::ffidl::info format dword] 0]


set rtn [_GetSerialNumber serial]
if {[ResultERR $rtn name info]} {
return -code error -errorinfo $info -errorcode [list TIEPIE $name $info]
}

binary scan $serial [::ffidl::info format dword] serial
return $serial
}


I wasn't setting the proper size of the byte array initially. The type is a
dword, but I had it set for word by accident. This might actually work :)

For me, ffidl is not worth the time investment, as I understand how to write
extensions in C just fine.

(bin) 32 % ::tiepie::InitInstrument
(bin) 33 % ::tiepie::GetSerialNumber
11740

^- Correct behavior!

now I unplug the unit to check for proper error return:

(bin) 34 % catch {::tiepie::GetSerialNumber}
(bin) 35 % 1
(bin) 36 % set errorInfo
Hardware is not connected
invoked from within
"::tiepie::GetSerialNumber"
(bin) 36 % set errorCode
TIEPIE E_NO_HARDWARE {Hardware is not connected}

^- Correct behavior!


So far, there are some limits I see. One particular function I want to use takes
a HANDLE type of a win32 waitable event. There is no such way to use ffidl for
such a thing as it is too low-level. I could abstract such a command, but then
I'm writing it in C anyways..

I could complete this library using ffidl, but I'd have to use the polling method
for event checking which would be lossy in terms of performance.

--
"It's great to have a friend who appreciates an earnest discussion of
ideas." -Calvin

signature.asc

JesusCh...@gmail.com

unread,
Apr 15, 2007, 3:50:16 AM4/15/07
to
> signature.asc
> 1KDownload

Well, I guess my post from Friday Night (or was it Saturday Morning)
didn't go through. Well David, you may win the bet. I found my old
Brent Welch's book and looked at his example and yours to get an idea
how the TCL C API works. I think I have a handle of it. Got concern
when I could find a Tcl_Get???FromObj for unsigned short, unitl I
noticed you provided a link for the TCL Library. After sum reading, I
noticed that Tcl_GetIntFromObj should do it. So far I'm only
attempting to "hand glue" the code for 'Connect' and 'Disconnect'
prototypes. If I can get this to work, (To Compile, to Load and talk
to the device without any crashes) would be a major relief. I'm still
getting some errors but I'm trying to resolve them (forcing me to re-
learn C again). But if I can't get it to work, I plan to post it
somewhere and see if someone can tell me what the hell I'm doing. I
been so spoiled by TCL, BASH, and C-SHELL, that I totally forgot the
concept of character arrays.

Question: Brent's book mentions to build two prototypes and
definitions:
Tcl_CreateCommand
Tcl_CreateObjCommand
Should I bother to create both?

Question: The second arguement (parameter) for the function
Tcl_CreateObjCommand; is this the name of the command that I will use
via TCL, to call the C Function?

Question: I'm getting a few of these warnings: "jnior310tcl.c(215) :
warning C4020: 'Tcl_GetStringFromObj' : too many actual parameters"
The examples I found, use three parameters but the "tcl.h" defines the
protoype with only two parameters. Should I been concern? And What
the Hell! passing two arguements when it expects two! This warning
message also applies to other Tcl_GetXXXXX calls.

Question (last one): Posting the project for someone to take a crack
at the 'Connect' and 'Disconnect' prototypes to much?

BTW: David, The two prototypes "hand glue" are about 275 lines
(included blank lines and comments), I got 56 more to go (once I get
things working), but man!!! You had to do 88, that is a lot. Still
wishing CPPTCL would had work for me, but the MS VC++ compiler just
couldn't handle BOOST.

Alexandre Ferrieux

unread,
Apr 15, 2007, 7:30:16 AM4/15/07
to
On 15 avr, 01:24, David Gravereaux <davyg...@pobox.com> wrote:
>
> For me, ffidl is not worth the time investment, as I understand how to write
> extensions in C just fine.

Yes, I understand; however, there are situations where the C skill is
not the only critical resource. For example, on some embedded devices
with exotic OSes like WinCE (yes, WinCE is exotic), the compile-test
cycle is... quite a cycle (not including the initial setup time to
install the proper environment on the PC). In these cases, FFidl or
other DLL callers can be very interesting.

> So far, there are some limits I see. One particular function I want to use takes
> a HANDLE type of a win32 waitable event. There is no such way to use ffidl for

> such a thing as it is too low-level. ...


> I could complete this library using ffidl, but I'd have to use the polling method
> for event checking which would be lossy in terms of performance.

OK, good point. This one is tricky not because of ffidl, but because
of the absence of a proper entry point in the Tcl notifier to use an
externally provided waitable object. But maybe we could add one ?
Does it deserve a TIP ? (not sure considering what's already on the
plate ;-)

Thanks for trying anyway !

-Alex

Kevin Kenny

unread,
Apr 15, 2007, 9:44:44 AM4/15/07
to
JesusCh...@gmail.com wrote:
> Question: Brent's book mentions to build two prototypes and
> definitions:
> Tcl_CreateCommand
> Tcl_CreateObjCommand
> Should I bother to create both?

Surely not. If you do, the second will override the first.
Tcl_CreateObjCommand is preferable if your arguments have any other
type than strings (for instance, if something accepts a number,
a variable name, a -keyword), because it avoids a lot of string
conversion.

> Question: The second arguement (parameter) for the function
> Tcl_CreateObjCommand; is this the name of the command that I will use
> via TCL, to call the C Function?

Yes.

> Question: I'm getting a few of these warnings: "jnior310tcl.c(215) :
> warning C4020: 'Tcl_GetStringFromObj' : too many actual parameters"
> The examples I found, use three parameters but the "tcl.h" defines the
> protoype with only two parameters. Should I been concern? And What
> the Hell! passing two arguements when it expects two! This warning
> message also applies to other Tcl_GetXXXXX calls.

I'm confused. Tcl_GetStringFromObj accepts two parameters: the
Tcl_Obj* and the pointer to the integer that will hold the string
length. Tcl_GetIntFromObj accepts the interpreter pointer because
it may need to store an error message. Tcl_GetString accepts only
the Tcl_Obj.

> Question (last one): Posting the project for someone to take a crack
> at the 'Connect' and 'Disconnect' prototypes to much?

Give a man a fish, you feed him for a day. Teach a man to fish, you
feed him for a lifetime. That said, we're surely willing to help.

Obviously, I'm not familiar with the library you want to wrap.
Does it support multiple active connections, with 'Connect' returning
something by pointer? If that's the case, the most usual pattern
is for Connect to create a command whose ClientData holds the
pointer, and have the second arg to that command be the thing
to do with the connection:

set connection [Connect arguments...]
$connection doSomething
$connection doSomethingElse
$connection disconnect

All the Tk widgets, for instance, work that way. It's much
easier than inventing your own way to represent pointers to
the Tcl code.
--
73 de ke9tv/2, Kevin

Kevin Kenny

unread,
Apr 15, 2007, 11:30:30 AM4/15/07
to
Alexandre Ferrieux wrote:
> OK, good point. This one is tricky not because of ffidl, but because
> of the absence of a proper entry point in the Tcl notifier to use an
> externally provided waitable object. But maybe we could add one ?
> Does it deserve a TIP ? (not sure considering what's already on the
> plate ;-)

Hmmm. It's fundamentally a non-portable operation (I can't see making
such a thing work on Unix, where it wouldn't mix well with select()
or even poll()), and we try to keep such things out of the public API.
But I can see some potential value to it. I suppose that the prospective
entry point would accept the waitable object, a callback, and client
data and launch a callback when the object is signalled? And then
another entry point to unregister the callback.

Then we'd have to look at the code to watch out for the "gotcha"
mentioned in the manual:

Note that MsgWaitForMultipleObjectsEx does not return if there is
unread input of the specified type in the message queue after the
thread has called a function to check the queue. This is because
functions such as PeekMessage, GetMessage, WaitMessage,
MsgWaitForMultipleObjects, and MsgWaitForMultipleObjectsEx check the
queue and then change the state information for the queue so that the
input is no longer considered new. A subsequent call to
MsgWaitForMultipleObjectsEx will not return until new input of the
specified type arrives. The existing unread input is ignored.

I'm starting to be of two minds about this - there seems to be a
potential can of worms here. Moreover, adding an API for callbacks on
waitable objects would fundamentally be a performance tweak. You can
achieve the same end by having another thread wait on the object or
objects, and send a message back to the main thread when the object is
signalled.

I'm willing to be convinced this is a good idea, but I'm not quite
convinced just yet.

David Gravereaux

unread,
Apr 15, 2007, 3:49:08 PM4/15/07
to
Alexandre Ferrieux wrote:
> On 15 avr, 01:24, David Gravereaux <davyg...@pobox.com> wrote:
>> For me, ffidl is not worth the time investment, as I understand how to write
>> extensions in C just fine.
>
> Yes, I understand; however, there are situations where the C skill is
> not the only critical resource. For example, on some embedded devices
> with exotic OSes like WinCE (yes, WinCE is exotic), the compile-test
> cycle is... quite a cycle (not including the initial setup time to
> install the proper environment on the PC). In these cases, FFidl or
> other DLL callers can be very interesting.


Yes, it definitely is interesting. I like how ffidl uses a byte array object as
writable space, and you [binary scan ..] it on return to pull it back in.


>> So far, there are some limits I see. One particular function I want to use takes
>> a HANDLE type of a win32 waitable event. There is no such way to use ffidl for
>> such a thing as it is too low-level. ...
>> I could complete this library using ffidl, but I'd have to use the polling method
>> for event checking which would be lossy in terms of performance.
>
> OK, good point. This one is tricky not because of ffidl, but because
> of the absence of a proper entry point in the Tcl notifier to use an
> externally provided waitable object. But maybe we could add one ?
> Does it deserve a TIP ? (not sure considering what's already on the
> plate ;-)


I don't see it as a limit as everything I need is available from C. Yes, there
isn't direct access to the notifier's platform specific waitable object, but the
platform neutral interface is workable.

Just for fun, this was how I handled the event code. The entry is
[tiepie::SetDataReadyCallback script] which takes a script to run when the ADC
completes a sampling set and is ready to be read. The HANDLE one I'm not using at
the moment until I do some more testing to find my "happy place" with the
interface. Instead, I'm using the function callback one which when data is ready,
calls the callback function (instead of setting a HANDLE event).

As this is a function entry outside the normal execution path of Tcl, the safest
way is to use Tcl_AsyncMark/Tcl_QueueEvent as shown below. Event sources involve
a bit more code, and thought this was more direct, though one could go that route
also.

TDataReady DataReady;
Tcl_AsyncProc DataReadyYield;
Tcl_EventProc DataReadyEval;

typedef struct {
Tcl_Interp *interp;
Tcl_Obj *script;
} DataReadyInfo;

typedef struct {
Tcl_Event header;
DataReadyInfo *info;
} DataReadyEvent;

DataReadyInfo dataReadyInfo;
Tcl_AsyncHandler dataReadyToken = NULL;


int
SetDataReadyCallbackCmd (ClientData clientData, Tcl_Interp *interp,


int objc, struct Tcl_Obj * CONST * objv)
{
word ok;

if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "script");
return TCL_ERROR;
}

if (dataReadyToken != NULL) {
Tcl_DecrRefCount(dataReadyInfo.script);
Tcl_AsyncDelete(dataReadyToken);
}
dataReadyInfo.interp = interp;
Tcl_IncrRefCount(dataReadyInfo.script = objv[1]);
dataReadyToken = Tcl_AsyncCreate(DataReadyYield, &dataReadyInfo);

ok = SetDataReadyCallback(&DataReady);
if (TiePieResultOK(interp, ok)) {
return TCL_OK;
}
return TCL_ERROR;
}

/* called from HS3.dll when data is ready */
void
DataReady (void)
{
ADC_Abort(); /* stop continuous sampling; just do a one shot. */
-> Tcl_AsyncMark(dataReadyToken);
}


/* the Tcl_AsyncProc, just queue an event to eval later when safe */
int
DataReadyYield (ClientData clientData, Tcl_Interp *interp, int code)
{
DataReadyEvent *evPtr;
DataReadyInfo *info = (DataReadyInfo *) clientData;

/* Not safe to eval in here, so don't do it! */

evPtr = (DataReadyEvent *) ckalloc(sizeof(DataReadyEvent));
evPtr->header.proc = DataReadyEval;
evPtr->info = info;
-> Tcl_QueueEvent((Tcl_Event *)evPtr, TCL_QUEUE_TAIL);
return code;
}

int
DataReadyEval (Tcl_Event *evPtr, int flags)
{
Tcl_Interp *interp = ((DataReadyEvent *)evPtr)->info->interp;
Tcl_Obj *script = ((DataReadyEvent *)evPtr)->info->script;

if (!(flags & TCL_FILE_EVENTS)) {
/* deferred, just file events, please */
return 0;
}

/* eval in proper interp. */
if (!Tcl_InterpDeleted(interp)) {
int result;

Tcl_Preserve(interp);
/* Locked */

-> result = Tcl_EvalObj(interp, script);

if (result == TCL_ERROR) {
char buff[60];
snprintf(buff, 60, "\nError in tpscope's DataReady script \"%s\"",
Tcl_GetStringFromObj(script, 0L));
Tcl_AddObjErrorInfo(interp, buff, -1);
Tcl_BackgroundError(interp);
}

Tcl_Release(interp);
/* Unlocked */
}

return 1;
}


At the moment, the script that uses this looks like this:


proc DataReady {} {
set ::dataReady [tiepie::ADC_GetDataVoltCh 1]
}

proc InitScope {} {
....
# allow for callback when data is ready to be read
tiepie::SetDataReadyCallback DataReady
....
}

proc SetGenVoltsAndGetDrainVolts {volts} {
# Set Gen amplitude
tiepie::SetFuncGenAmplitude $volts

# Kick up a measurement set
tiepie::ADC_Start

# try doing something productive during the wait
vwait ::dataReady

return [averageOfSnapshot $::dataReady]
}

--
My life needs a rewind/erase button.
-- Calvin

signature.asc

David Gravereaux

unread,
Apr 15, 2007, 4:29:32 PM4/15/07
to
Kevin Kenny wrote:
> JesusCh...@gmail.com wrote:

>> Question (last one): Posting the project for someone to take a crack
>> at the 'Connect' and 'Disconnect' prototypes to much?
>
> Give a man a fish, you feed him for a day. Teach a man to fish, you
> feed him for a lifetime. That said, we're surely willing to help.

Don't miss the fish:
news:PYWdnWNf7t7-SoLb...@comcast.com or
http://groups.google.com/group/comp.lang.tcl/search?group=comp.lang.tcl&q=PYWdnWNf7t7-SoLbnZ2dnUVZ_gKdnZ2d%40comcast.com&qt_g=Search+this+group

Though it doesn't seem that google has picked it up.

--
What do you get when you cross a cantaloupe with lassie? A melon-collie
baby! Get it?? HA HA HA OH OH HA HA! -- Calvin

signature.asc

Alexandre Ferrieux

unread,
Apr 15, 2007, 6:02:53 PM4/15/07
to
On Apr 15, 5:30 pm, Kevin Kenny <kenn...@acm.org> wrote:
> > ...

> > of the absence of a proper entry point in the Tcl notifier to use an
> > externally provided waitable object. But maybe we could add one ?
> > Does it deserve a TIP ? (not sure considering what's already on the
> > plate ;-)
>
> Hmmm. It's fundamentally a non-portable operation (I can't see making
> such a thing work on Unix, where it wouldn't mix well with select()
> or even poll()), and we try to keep such things out of the public API.

Right. Now in unix there are signals, which have also been kept
outside Tcl for the same reasons.
What about being generous, and giving a way of using the idiomatic
objects of each OS, but through a unified script-level API (old good
vwait and handlers) ? Wouldn't it be cool if

unix_signal 2 {puts "You hit Ctrl-C"}
vwait forever

and

windows_waitable_object 0xdeadbeef {puts "The Foobar has just
Bazed"}
vwait forever

did work in the respective OSes ? (select better names for the
primitives ;-)

> But I can see some potential value to it. I suppose that the prospective
> entry point would accept the waitable object, a callback, and client
> data and launch a callback when the object is signalled? And then
> another entry point to unregister the callback.
>
> Then we'd have to look at the code to watch out for the "gotcha"
> mentioned in the manual:
>
> Note that MsgWaitForMultipleObjectsEx does not return if there is
> unread input of the specified type in the message queue after the
> thread has called a function to check the queue. This is because
> functions such as PeekMessage, GetMessage, WaitMessage,
> MsgWaitForMultipleObjects, and MsgWaitForMultipleObjectsEx check the
> queue and then change the state information for the queue so that the
> input is no longer considered new. A subsequent call to
> MsgWaitForMultipleObjectsEx will not return until new input of the
> specified type arrives. The existing unread input is ignored.

This gotcha, unless I'm mistaken, is exactly the same as the well-
known stdio/select issue:
An unwise implementation doing select() on an fd, and having wrapped a
stdio FILE * over it (fdopen), reading it with fread() or fgets(). If
data come too fast, the fgets() swallows several lines worth in the
stdio buffer, and a stubborn call of select() immediately afterwards
will block until new data arrive, thus ignoring the additional,
buffered lines. This can be solved in unix, but non-portably, at least
on Solaris, AIX and Linux, using the (accidentally) non-opaque FILE
struct. The field _cnt (I don't remember in which one of the three)
gives the number of buffered-ahead, un(f)read bytes.

I know this is an ugly workaround but I use it all the time, because
(1) the three unices mentioned cover most of my universe, and (2) in
Windows I use Tcl everywhere... I also understand why Tcl doesn't,
(which implies not using stdio in the IO code).

I know next to nothing about Windows, but by making a parallel with
the above trick, it looks like calling PeekMessage in a loop until it
yields nothing, would do the same. Wouldn't it ?

> I'm starting to be of two minds about this - there seems to be a
> potential can of worms here. Moreover, adding an API for callbacks on
> waitable objects would fundamentally be a performance tweak. You can
> achieve the same end by having another thread wait on the object or
> objects, and send a message back to the main thread when the object is
> signalled.

You are right. Any trick avoiding *polling* and bringing back all
these exotic event sources to a more central object of the notifier,
would do the job. But since on windows the notifier uses
WaitForMultipleObject et al., it seems natural to insert in its list
of waitable objects ... a new waitable object. Symmetrically, in unix
a signal would awaken the select (interrupted syscall). In both cases
the event would be serviced directly from the main thread. Having no
extra thread when not needed is a very interesting feature IMHO...

I do realize the cost of non-portability, but isn't it more acceptable
if it amounts to a dichotomy unix/windows, considering this dichotomy
has already existed in the core for years ?

-Alex

David Gravereaux

unread,
Apr 15, 2007, 7:35:43 PM4/15/07
to
Alexandre Ferrieux wrote:
> On Apr 15, 5:30 pm, Kevin Kenny <kenn...@acm.org> wrote:
>>> ...
>>> of the absence of a proper entry point in the Tcl notifier to use an
>>> externally provided waitable object. But maybe we could add one ?
>>> Does it deserve a TIP ? (not sure considering what's already on the
>>> plate ;-)
>> Hmmm. It's fundamentally a non-portable operation (I can't see making
>> such a thing work on Unix, where it wouldn't mix well with select()
>> or even poll()), and we try to keep such things out of the public API.
>
> Right. Now in unix there are signals, which have also been kept
> outside Tcl for the same reasons.
> What about being generous, and giving a way of using the idiomatic
> objects of each OS, but through a unified script-level API (old good
> vwait and handlers) ? Wouldn't it be cool if
>
> unix_signal 2 {puts "You hit Ctrl-C"}
> vwait forever
>
> and
>
> windows_waitable_object 0xdeadbeef {puts "The Foobar has just
> Bazed"}
> vwait forever
>
> did work in the respective OSes ? (select better names for the
> primitives ;-)


In the meantime, there is Tcl_ThreadAlert(). Though it isn't the same as adding a
HANDLE to the notifier's wait object list on win.

--
I'm killing time while I wait for life to shower me with meaning and
happiness. -- Calvin

signature.asc

David Gravereaux

unread,
Apr 15, 2007, 8:27:59 PM4/15/07
to
Alexandre Ferrieux wrote:
....

> But since on windows the notifier uses
> WaitForMultipleObject et al., it seems natural to insert in its list
> of waitable objects ... a new waitable object
....

The list of waitable objects is limited to 64 per thread. If you can limit the
hooking to under 64, then this could be helpful to others. To do more than 64,
the master would need to be a list of lists and newly created threads would wait
on each list to alert their object in the master. It gets ugly fast.

How it stands now with their only being a single Event object (or a windows
message after the first Tcl_ServiceModeHook(TCL_SERVICE_ALL) is called) works fine
IMO. Every channel driver can awaken the notify via Tcl_ThreadAlert from their
own manner to which they are alerted. I could see the convenience of by-passing
some of the mess and just giving a HANDLE that is alertable, such as the console
handle as it is a real waitable object. Although after awaking the notifier, code
would have to be added to call the thing who set the handle into it.

[This reminds me of some code I was working on a few years ago....]

Also, while the thread is idling in the notifier, it can still be used... Bwaa
haa haa..

Notice in win/tclWinNotify.c:Tcl_WaitForEvent() that the call to
MsgWaitForMultipleObjectsEx() uses the MWMO_ALERTABLE flag. What that means is
that the thread is available to perform APC [asynchronous procedure call]
callbacks while it is idle.

I don't know of any channel drivers that use it, but it is available.

--
Wormwood : Calvin, how about you?
Calvin : Hard to say ma'am. I think my cerebellum just fused.

signature.asc

David Gravereaux

unread,
Apr 15, 2007, 8:38:51 PM4/15/07
to
JesusCh...@gmail.com wrote:
> BTW: David, The two prototypes "hand glue" are about 275 lines
> (included blank lines and comments), I got 56 more to go (once I get
> things working), but man!!! You had to do 88, that is a lot. Still
> wishing CPPTCL would had work for me, but the MS VC++ compiler just
> couldn't handle BOOST.


2.2KLOC in my scope glue and counting.. Yeah, it's long, but you get into a
groove past the first few and you'll notice you'll copy a lot of the code you
already wrote.

signature.asc

JesusCh...@gmail.com

unread,
Apr 16, 2007, 9:01:48 PM4/16/07
to
On Apr 15, 5:38 pm, David Gravereaux <davyg...@pobox.com> wrote:
> signature.asc
> 1KDownload

Hi David,

Well, after many attempts I'm no longer getting compiler errors for my
C file, but a few warnings. When it tries to link, it fails. BTW:
I'm only using the "Tcl_CreateObjCommand" and not the
"Tcl_CreateCommand", which cuts the code in half.


file: jnior310tcl.c
=============================== START
//
// jnior310tcl.c
//
// The initialization procedure for a loadable package.
//

//
// Build Command
// cd "C:/Program Files/TCL_TK/tcl_c_api"


// "C:/Program Files/Microsoft Visual Studio 8/VC/bin/cl.exe"

jnior310tcl.c /EHs /I "C:/Program Files/Tcl/include" /I "C:/Program
Files/TCL_TK/tcl_c_api/integpg" /LD "C:/Program Files/Tcl/lib/
Tcl84.lib" /LD "C:/Program Files/TCL_TK/tcl_c_api/integpg/
jnior300.lib" > error02.txt
//

#include <tcl.h>
#include "integpg/jnior_300.h"

//#define _SCL_SECURE_NO_DEPRECATE
//#define _CRT_SECURE_NO_DEPRECATE

//
============================================================================================

//
// Declarations for application-specific command procedures
//

//
// long __stdcall Connect(char *ip, unsigned short port, long *handle,
char *user_name, char *password);
//
int ConnectObjCmd(ClientData clientData,
Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[]);

//
// long __stdcall Disconnect(long handle);
//
int DisconnectObjCmd(ClientData clientData,
Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[]);

//
// Jnior310tcl_Init is called when the package is loaded.
//
int Jnior310tcl_Init(Tcl_Interp *interp) {
//
// Initialize the stub table interface.
//


if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
return TCL_ERROR;
}

//
// Register one (not two0 variation(s) of Connect.
// The tclObjtclConnect command uses the object interface.
//

//
// long __stdcall Connect(char *ip, unsigned short port, long


*handle, char *user_name, char *password);

//
Tcl_CreateObjCommand(interp, "Connect", ConnectObjCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);

//
// long __stdcall Disconnect(long handle);
//
Tcl_CreateObjCommand(interp, "Disconnect", DisconnectObjCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);


//
// Declare that we implement the jnior310tcl package
// so scripts that do "package require jnior310tcl"
// can load the library automatically.
//
Tcl_PkgProvide(interp, "jnior310tcl", "1.1");
return TCL_OK;
}


//
============================================================================================

//
// jnior310tcl Project
//

//
// The ConnectObjCmd C command procedure.
// long __stdcall Connect(char *ip, unsigned short port, long *handle,
char *user_name, char *password);
//

int
ConnectObjCmd(ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
Tcl_Obj *resultPtr;

int ConnectResult;

char *ip4TCL;
unsigned short port4TCL;
long handle4TCL;
char *user_name4TCL;
char *password4TCL;

if (objc != 5) {
Tcl_WrongNumArgs(interp, 1, objv, "ip, port, handle, user_name,
password");
return TCL_ERROR;
}

if (Tcl_GetStringFromObj(interp, objv[1], &ip4TCL) != TCL_OK) {
return TCL_ERROR;
}
// Should it be Tcl_GetUnsignedShortFromObj?
if (Tcl_GetIntFromObj(interp, objv[2], &port4TCL) != TCL_OK) {
return TCL_ERROR;
}
if (Tcl_GetLongFromObj(interp, objv[3], &handle4TCL) != TCL_OK) {
return TCL_ERROR;
}
if (Tcl_GetStringFromObj(interp, objv[4], &user_name4TCL) != TCL_OK)
{
return TCL_ERROR;
}
if (Tcl_GetStringFromObj(interp, objv[5], &password4TCL) != TCL_OK) {
return TCL_ERROR;
}

ConnectResult = Connect(ip4TCL, port4TCL, handle4TCL, user_name4TCL,
password4TCL);

resultPtr = Tcl_GetObjResult(interp);
Tcl_SetIntObj(resultPtr, ConnectResult);
return TCL_OK;
}

//
============================================================================================

//
// jnior310tcl Project
//

//
// The DisconnectObjCmd C command procedure.
// long __stdcall Disconnect(long handle);
//

int
DisconnectObjCmd(ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
Tcl_Obj *resultPtr;

int DisconnectResult;

long handle4TCL;

if (objc != 1) {
Tcl_WrongNumArgs(interp, 1, objv, "handle");
return TCL_ERROR;
}

if (Tcl_GetLong(interp, objv[1], handle4TCL) != TCL_OK) {
return TCL_ERROR;
}

DisconnectResult = Disconnect(handle4TCL);

resultPtr = Tcl_GetObjResult(interp);
Tcl_SetIntObj(resultPtr, DisconnectResult);
return TCL_OK;
}


=============================== STOP


ERROR/WARNING MESSAGES
=============================== START
jnior310tcl.c
jnior310tcl.c(108) : warning C4133: 'function' : incompatible types -
from 'Tcl_Interp *' to 'Tcl_Obj *'
jnior310tcl.c(108) : warning C4133: 'function' : incompatible types -
from 'Tcl_Obj *const ' to 'int *'
jnior310tcl.c(108) : warning C4020: 'Tcl_GetStringFromObj' : too many
actual parameters
jnior310tcl.c(112) : warning C4133: 'function' : incompatible types -
from 'unsigned short *' to 'int *'
jnior310tcl.c(118) : warning C4133: 'function' : incompatible types -
from 'Tcl_Interp *' to 'Tcl_Obj *'
jnior310tcl.c(118) : warning C4133: 'function' : incompatible types -
from 'Tcl_Obj *const ' to 'int *'
jnior310tcl.c(118) : warning C4020: 'Tcl_GetStringFromObj' : too many
actual parameters
jnior310tcl.c(121) : warning C4133: 'function' : incompatible types -
from 'Tcl_Interp *' to 'Tcl_Obj *'
jnior310tcl.c(121) : warning C4133: 'function' : incompatible types -
from 'Tcl_Obj *const ' to 'int *'
jnior310tcl.c(121) : warning C4020: 'Tcl_GetStringFromObj' : too many
actual parameters
jnior310tcl.c(125) : warning C4047: 'function' : 'long *' differs in
levels of indirection from 'long'
jnior310tcl.c(125) : warning C4024: 'Connect' : different types for
formal and actual parameter 3
c:\program files\tcl_tk\tcl_c_api\jnior310tcl.c(158) : warning C4700:
uninitialized local variable 'handle4TCL' used
Microsoft (R) Incremental Linker Version 8.00.50727.42
Copyright (C) Microsoft Corporation. All rights reserved.

/out:jnior310tcl.dll
/dll
/implib:jnior310tcl.lib
jnior310tcl.obj
"C:/Program Files/Tcl/lib/Tcl84.lib"
"C:/Program Files/TCL_TK/tcl_c_api/integpg/jnior300.lib"
jnior310tcl.obj : error LNK2019: unresolved external symbol
_Connect@20 referenced in function _ConnectObjCmd
jnior310tcl.obj : error LNK2019: unresolved external symbol
_Disconnect@4 referenced in function _DisconnectObjCmd
jnior310tcl.obj : error LNK2019: unresolved external symbol
_Tcl_GetLong referenced in function _DisconnectObjCmd
jnior310tcl.dll : fatal error LNK1120: 3 unresolved externals
=============================== STOP

David Gravereaux

unread,
Apr 16, 2007, 11:45:16 PM4/16/07
to
I unfortunately have way too much to comment on. So here's my post again (that
google didn't pick up) of my stab at those two.

Let's hand roll those two. I'll assume the return code is an error code, with
zero being success and *handle is set by the function on return. The commands in
the interp will be [jnior::Connect <ip> <port> <user> <pass>], it returns the
handle of the connection. [jnior::Disconnect <handle>] will do the disconnect
called with the handle returned from Connect.

command to compile would be something like this:

cl -nologo -MD -IC:\tcl\include jnior_tcl.c -link -dll -out:jniortcl.dll
-libpath:C:\Tcl\lib jnior300.lib

--- jnior_tcl.c -----

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <limits.h>
#include "jnior_300.h"
#define USE_TCL_STUBS
#include "tcl.h"

/* Mark this .obj as needing Tcl's Stubs library. */
#pragma comment(lib, "tclstub" \
STRINGIFY(JOIN(TCL_MAJOR_VERSION,TCL_MINOR_VERSION)) ".lib")

/* Protos */
__declspec(dllexport) Tcl_AppInitProc Jniortcl_Init;
Tcl_ObjCmdProc ConnectCmd;
Tcl_ObjCmdProc DisconnectCmd;

/* Optional, but explicit. */
BOOL WINAPI
DllMain (HINSTANCE hInst, ULONG ulReason, LPVOID lpReserved)
{
if (ulReason == DLL_PROCESS_ATTACH) DisableThreadLibraryCalls(hInst);
return TRUE;
}

int
Jniortcl_Init (Tcl_Interp *interp)
{
#ifdef USE_TCL_STUBS


if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
return TCL_ERROR;
}

#endif

# define MAKECMD(name) \
Tcl_CreateObjCommand(interp, STRINGIFY(JOIN(jnior::,name)), \
JOIN(name,Cmd), 0L, 0L);

MAKECMD(Connect);
MAKECMD(Disconnect);

Tcl_PkgProvide(interp, "jnior", "0.1");
return TCL_OK;
}

int
ConnectCmd (ClientData clientData, Tcl_Interp *interp,


int objc, struct Tcl_Obj * CONST * objv)
{

long ok, handle;
char *ip, *username, *password;
unsigned short port;
int iPort;

if (objc != 5) {
Tcl_WrongNumArgs(interp, 1, objv, "ip port user pass");
return TCL_ERROR;
}

ip = Tcl_GetString(objv[1]);

if (Tcl_GetIntFromObj(interp, objv[2], &iPort) != TCL_OK) {
return TCL_ERROR;
}
if (iPort < 0 || iPort > USHRT_MAX) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("port out-of-range",-1));
return TCL_ERROR;
}
port = (unsigned short) iPort;

user_name = Tcl_GetString(objv[3]);
password = Tcl_GetString(objv[4]);

ok = Connect(ip, port, &handle, user_name, password);

if (ok == 0) {
Tcl_SetObjResult(interp, Tcl_NewLongObj(handle));
return TCL_OK;
} else {
/* handle error condition here */
return TCL_ERROR;
}
}

int
DisconnectCmd (ClientData clientData, Tcl_Interp *interp,


int objc, struct Tcl_Obj * CONST * objv)
{

long ok, handle;

if (objc != 2) {


Tcl_WrongNumArgs(interp, 1, objv, "handle");
return TCL_ERROR;
}

if (Tcl_GetLongFromObj(interp, objv[1], &handle) != TCL_OK) {
return TCL_ERROR;
}

ok = Disconnect(handle);
if (ok == 0) {
/* success */
return TCL_OK;
}
return TCL_ERROR;
}

--
How many boards would the Mongols hoard if the Mongol hordes got bored?
-- Calvin

signature.asc

JesusCh...@gmail.com

unread,
Apr 17, 2007, 8:55:56 PM4/17/07
to
> signature.asc
> 1KDownload

David,

I got one more question. Do I have to tell the compiler to load the
jnior.dll or does it have to be part of the source code. Here are the
error messages that I'm getting. What compiler are you using?

error04.txt
===================================== START
jnior310tcl2.c
c:\program files\tcl_tk\tcl_c_api2\jnior_300.h(19) : warning C4031:
second formal parameter list longer than the first list
c:\program files\tcl_tk\tcl_c_api2\jnior_300.h(19) : warning C4028:
formal parameter 1 different from declaration
c:\program files\tcl_tk\tcl_c_api2\jnior_300.h(19) : warning C4142:
benign redefinition of type
c:\program files\tcl_tk\tcl_c_api2\jnior_300.h(19) : warning C4273:
'GetVersion' : inconsistent dll linkage
C:\Program Files\Microsoft Visual Studio\VC98\include
\winbase.h(1116) : see previous definition of 'GetVersion'
Creating library jniortcl2.lib and object jniortcl2.exp
jnior310tcl2.obj : error LNK2019: unresolved external symbol
_Connect@20 referenced in function _ConnectCmd
jnior310tcl2.obj : error LNK2019: unresolved external symbol
_Disconnect@4 referenced in function _DisconnectCmd
jniortcl2.dll : fatal error LNK1120: 2 unresolved externals
===================================== STOP


I guess my biggest problem is with the compiler. I got IT to re-
installed MS Visual Studio 6 (because we have a spare license), and
that just gave me more problems, and I then had IT re-installed MS VC+
+ 2005 "Express Edition", since it has a 30 day trial, but it is
missing the "window.h", so now I'm trying to link libraries from both
compilers.

-JC

David Gravereaux

unread,
Apr 17, 2007, 10:49:33 PM4/17/07
to
JesusCh...@gmail.com wrote:

> David,
>
> I got one more question. Do I have to tell the compiler to load the
> jnior.dll or does it have to be part of the source code.

It needs to be part of the link. That's what the .lib file is for.

> Here are the
> error messages that I'm getting. What compiler are you using?

MSVC++ 6 (an oldie, but works for me)

> error04.txt
> ===================================== START
> jnior310tcl2.c
> c:\program files\tcl_tk\tcl_c_api2\jnior_300.h(19) : warning C4031:
> second formal parameter list longer than the first list
> c:\program files\tcl_tk\tcl_c_api2\jnior_300.h(19) : warning C4028:
> formal parameter 1 different from declaration
> c:\program files\tcl_tk\tcl_c_api2\jnior_300.h(19) : warning C4142:
> benign redefinition of type
> c:\program files\tcl_tk\tcl_c_api2\jnior_300.h(19) : warning C4273:
> 'GetVersion' : inconsistent dll linkage
> C:\Program Files\Microsoft Visual Studio\VC98\include
> \winbase.h(1116) : see previous definition of 'GetVersion'

^-- All problems with the jnior header file's definition of GetVersion(). The
same function name is exported by windows, too. You could get around it by using
C++ namespaces like so:

namespace jnior {
# include "jnior_300.h"
}

Then the function would be jnior::GetVersion() and wouldn't clash. All other
jnior functions and types would be in that namespace as well. How the linker
would resolve that, I'm not sure off-hand.


> Creating library jniortcl2.lib and object jniortcl2.exp
> jnior310tcl2.obj : error LNK2019: unresolved external symbol
> _Connect@20 referenced in function _ConnectCmd
> jnior310tcl2.obj : error LNK2019: unresolved external symbol
> _Disconnect@4 referenced in function _DisconnectCmd
> jniortcl2.dll : fatal error LNK1120: 2 unresolved externals
> ===================================== STOP


Make sure jnior300.lib is in your list of libraries you link to and the path to
find it is there too.


> I guess my biggest problem is with the compiler. I got IT to re-
> installed MS Visual Studio 6 (because we have a spare license), and
> that just gave me more problems, and I then had IT re-installed MS VC+
> + 2005 "Express Edition", since it has a 30 day trial, but it is
> missing the "window.h", so now I'm trying to link libraries from both
> compilers.
>
> -JC

Cross-pollinating compilers voids your warranty ;) Just joking, should work fine.
There's always the "Platform SDK" from MSDN if you need the window.h stuff, but
it's rather a big download as I remember.

About you only get using an IDE environment is the ability to step-debug your
application, which may or may not be needed for your project. I like free
compilers, myself.. there's nothing wrong with them.

--
"But the important thing is persistence." -Calvin trying to juggle eggs

signature.asc

JesusCh...@gmail.com

unread,
Apr 18, 2007, 4:47:38 AM4/18/07
to
On Apr 17, 7:49 pm, David Gravereaux <davyg...@pobox.com> wrote:
> signature.asc
> 1KDownload

Right after my last post, I looked through the groups and learned that
the error message: "error LNK2019: unresolved external symbol ..." is
due to the lack of the "SDK Files" (which is why windows.h was not
included). I verified other post and it appears that it comes down to
that I really need these files. Specifically:
User32.Lib
Gdi32.Lib
WinSpool.Lib
ComDlg32.Lib
AdvAPI32.Lib
Shell32.Lib
Ole32.Lib
OleAut32.Lib
Uuid.Lib

I had to download the SDK and followed the instructions from here:
http://msdn.microsoft.com/vstudio/express/visualc/usingpsdk/default.aspx

I ran the test and things worked out as expected.

Since I'm using the Command Prompt rather than the GUI to compile the
code, I had to create a .bat file to set the Environment.

add_sdk_paths.bat
================== START
echo Adding SDK paths

@set PATH=C:\Program Files\Microsoft Platform SDK for Windows Server
2003 R2\Bin;%PATH%
@set INCLUDE=C:\Program Files\Microsoft Platform SDK for Windows
Server 2003 R2\Include;%INCLUDE%
@set LIB=C:\Program Files\Microsoft Platform SDK for Windows Server
2003 R2\Lib;%LIB%
================== STOP

Once I execute the Visual Studio 2005 Command Prompt, I execute
the .bat file and verify my paths
dosPrompt> echo %PATH%

I then tried to compile your source code and I kept getting the same
errors. I then tried my source code, with the corrections you
provided and I only get the "error LNK2019: unresolved external
symbol....." error message.

BTW: I did tried to add "namespace jnior { #include "integpg/
jnior_300.h" }" and created other errors where it didn't even compile.
Error Message:
jnior310tcl2.c
jnior310tcl2.c(4) : error C2061: syntax error : identifier 'jnior'
jnior310tcl2.c(4) : error C2059: syntax error : ';'
jnior310tcl2.c(4) : error C2449: found '{' at file scope (missing
function header?)
jnior310tcl2.c(4) : error C2014: preprocessor command must start as
first nonwhite space
jnior310tcl2.c(121) : fatal error C1004: unexpected end-of-file found


Any ways; here is the cl command used and the error message

using your source code, compile command:
cl -nologo -MD -I"C:/Program Files/Tcl/include" jnior310tcl2.c -link -
dll -out:jnior310tcl2.dll -libpath:"C:/Program Files/TCL_TK/tcl_c_api2/
integpg" jnior300.lib -libpath:"C:/Program Files/Tcl/lib" Tcl84.lib -
libpath:"C:\Program Files\Microsoft Platform SDK for Windows Server
2003 R2\Lib" Kernel32.Lib User32.Lib Gdi32.Lib WinSpool.Lib
ComDlg32.Lib AdvAPI32.Lib Shell32.Lib Ole32.Lib OleAut32.Lib Uuid.Lib
> error04a.txt

Error Message:
jnior310tcl2.c
c:\program files\tcl_tk\tcl_c_api2\integpg/jnior_300.h(19) : warning


C4031: second formal parameter list longer than the first list

c:\program files\tcl_tk\tcl_c_api2\integpg/jnior_300.h(19) : warning


C4028: formal parameter 1 different from declaration

c:\program files\tcl_tk\tcl_c_api2\integpg/jnior_300.h(19) : warning


C4142: benign redefinition of type

c:\program files\tcl_tk\tcl_c_api2\integpg/jnior_300.h(19) : warning


C4273: 'GetVersion' : inconsistent dll linkage

C:\Program Files\Microsoft Platform SDK for Windows Server
2003 R2\Include\winbase.h(1917) : see previous definition of
'GetVersion'
Creating library jnior310tcl2.lib and object jnior310tcl2.exp


jnior310tcl2.obj : error LNK2019: unresolved external symbol
_Connect@20 referenced in function _ConnectCmd
jnior310tcl2.obj : error LNK2019: unresolved external symbol
_Disconnect@4 referenced in function _DisconnectCmd

jnior310tcl2.dll : fatal error LNK1120: 2 unresolved externals


My source code, with your corrections, compile command:
cl jnior310tcl.c /EHs /I "C:/Program Files/Tcl/include" /I "C:/Program
Files/TCL_TK/tcl_c_api2/integpg" /LD "C:/Program Files/Tcl/lib/
Tcl84.lib" /LD "C:/Program Files/TCL_TK/tcl_c_api2/integpg/
jnior300.lib" /LD Kernel32.Lib /LD User32.Lib /LD Gdi32.Lib /LD
WinSpool.Lib /LD ComDlg32.Lib /LD AdvAPI32.Lib /LD Shell32.Lib /LD
Ole32.Lib /LD OleAut32.Lib /LD Uuid.Lib > error05.txt

Error Message:
jnior310tcl.c


Microsoft (R) Incremental Linker Version 8.00.50727.42
Copyright (C) Microsoft Corporation. All rights reserved.

/out:jnior310tcl.dll
/dll
/implib:jnior310tcl.lib
jnior310tcl.obj
"C:/Program Files/Tcl/lib/Tcl84.lib"

"C:/Program Files/TCL_TK/tcl_c_api2/integpg/jnior300.lib"
Kernel32.Lib
User32.Lib
Gdi32.Lib
WinSpool.Lib
ComDlg32.Lib
AdvAPI32.Lib
Shell32.Lib
Ole32.Lib
OleAut32.Lib
Uuid.Lib
jnior310tcl.obj : error LNK2019: unresolved external symbol
_Connect@20 referenced in function _ConnectObjCmd
jnior310tcl.obj : error LNK2019: unresolved external symbol
_Disconnect@4 referenced in function _DisconnectObjCmd
jnior310tcl.dll : fatal error LNK1120: 2 unresolved externals


At this point, I feel that I'm using the "cl" command wrong, or I need
to fix my Environment.

The suckiest part of the project is getting IT to install or uninstall
SW.

-JC

P.S. To David and everyone, I want to thank you for helping me out on
your free time.
BTW: I know I won't be working on this C Environment much longer, but
I do feel like an idiot, that I forgot my C, how to debug C/C++ and
especially how to use the compiler. Was wondering what type of class/
books/projects you folks recommend to get me started. Since this is
something I plan to work on, during my free time.

Can't wait to I start coding in TCL.

David Gravereaux

unread,
Apr 18, 2007, 2:39:30 PM4/18/07
to
JesusCh...@gmail.com wrote:

> BTW: I did tried to add "namespace jnior { #include "integpg/
> jnior_300.h" }" and created other errors where it didn't even compile.
> Error Message:
> jnior310tcl2.c
> jnior310tcl2.c(4) : error C2061: syntax error : identifier 'jnior'
> jnior310tcl2.c(4) : error C2059: syntax error : ';'
> jnior310tcl2.c(4) : error C2449: found '{' at file scope (missing
> function header?)
> jnior310tcl2.c(4) : error C2014: preprocessor command must start as
> first nonwhite space
> jnior310tcl2.c(121) : fatal error C1004: unexpected end-of-file found


I forgot to mention you'd have to change over to C++ by renaming the source file
with the extension .cpp instead of .c so the compiler switches modes.


> Error Message:
> jnior310tcl2.c
> c:\program files\tcl_tk\tcl_c_api2\integpg/jnior_300.h(19) : warning
> C4031: second formal parameter list longer than the first list
> c:\program files\tcl_tk\tcl_c_api2\integpg/jnior_300.h(19) : warning
> C4028: formal parameter 1 different from declaration
> c:\program files\tcl_tk\tcl_c_api2\integpg/jnior_300.h(19) : warning
> C4142: benign redefinition of type
> c:\program files\tcl_tk\tcl_c_api2\integpg/jnior_300.h(19) : warning
> C4273: 'GetVersion' : inconsistent dll linkage
> C:\Program Files\Microsoft Platform SDK for Windows Server
> 2003 R2\Include\winbase.h(1917) : see previous definition of
> 'GetVersion'


^- that's still the problem with <windows.h> having the same function name as one
contained in jnior_300.h. Just like the error says. What you could do is to not
#include <windows.h> at all. Tcl extensions can build without it as long as they
don't use any of the services. To be honest, you should contact the API authors
and ask them how to handle their error.


> Creating library jnior310tcl2.lib and object jnior310tcl2.exp
> jnior310tcl2.obj : error LNK2019: unresolved external symbol
> _Connect@20 referenced in function _ConnectCmd
> jnior310tcl2.obj : error LNK2019: unresolved external symbol
> _Disconnect@4 referenced in function _DisconnectCmd
> jnior310tcl2.dll : fatal error LNK1120: 2 unresolved externals


That part has me perplexed. jnior300.lib is included in the link, yet the linker
does resolve any function contained in it. I think you'd better ask the authors
about that. You can use the lib.exe tool to query what it contains. I'm assuming
it is an import library. If it's an import library made be Borland C++ builder,
it won't work with the MS toolchain. Even if its a static library, it should
resolve something.


--
It's psychosomatic. You need a lobotomy. I'll get a saw.
-- Calvin

signature.asc

JesusCh...@gmail.com

unread,
Apr 18, 2007, 9:12:57 PM4/18/07
to
On Apr 18, 11:39 am, David Gravereaux <davyg...@pobox.com> wrote:
> signature.asc
> 1KDownload

IT COMPILE!!!!!

I use namespace as you suggested and renamed the file to .cpp Got
zero warnings and error messages!!!

When I load it, I get the following error:
tclsh> load jnior310tcl2.dll
couldn't load library "jnior310tcl.dll": this library or a dependent
library could not be found in library path

Which appears to be an error because it depending on the SDK dll
(jnior300.dll). I append the path to auto_path and I still get the
error message. Looked through the groups, and there are two
possibilities:
1) My paths are wrong
2) The DLL has an internal error


BTW: I also tried to copy the jnior300.dll into "C:\Program Files\Tcl
\lib" and "C:\Program Files\Tcl\bin" and neither worked. I also
copied the file to "C:\WINDOWS\system" and "C:\WINDOWS\system32" and
nothing.

I remember way back when (1997) on windows NT, I was able to look at
DLL properties to see dependencies and where the dependencies should
exist. Just can remember the name of that tool.

Will investigate further!!

-JC

JesusCh...@gmail.com

unread,
Apr 18, 2007, 9:47:20 PM4/18/07
to
On Apr 18, 6:12 pm, JesusChuyCam...@gmail.com wrote:
> On Apr 18, 11:39 am, David Gravereaux <davyg...@pobox.com> wrote:
>
>
>
> > JesusChuyCam...@gmail.com wrote:
> > > BTW: I did tried to add "namespacejnior{ #include "integpg/

I executed "Depends" from the MS Platform SDK Tools to see analyze the
jnior310tcl2.dll. In there, i have two yellow question marks, one for
jnior300.dll and the other for msvcr80.dll. When I select either of
these dlls, I able to see the function name, but if i right click on
them to see the properties, I get an error message that it can not
find it.

David Gravereaux

unread,
Apr 18, 2007, 10:46:20 PM4/18/07
to
JesusCh...@gmail.com wrote:

> I executed "Depends" from the MS Platform SDK Tools to see analyze the
> jnior310tcl2.dll. In there, i have two yellow question marks, one for
> jnior300.dll and the other for msvcr80.dll. When I select either of
> these dlls, I able to see the function name, but if i right click on
> them to see the properties, I get an error message that it can not
> find it.
>

Good news, you're really close. Also check that the name of the Tcl_AppInitProc
function is correct for the name of the DLL. You might have to rename the
function to Jnior_Init() as I think the loader name detection stops at the start
of numbers.

--
You can present the material, but you can't make me care.
-- Calvin

signature.asc

JesusCh...@gmail.com

unread,
Apr 19, 2007, 4:56:04 AM4/19/07
to
On Apr 18, 7:46 pm, David Gravereaux <davyg...@pobox.com> wrote:
> signature.asc
> 1KDownload

By any chance when you build your DLL, did it depend on msvcr80.dll?
Once I copy both jnior300.dll and the msvcr80.dll to the same
directory as jnior310tcl2.dll the error message of "couldn't load
library ....: this library or a dependent library could not be found
in library path" went away but I got a new error message "could not
find the section that owns the import directory". When I look at the
msvcr80.dll with "depends" it is a shade of red; with some functions
green and others red. I have three of these DLLs: One for win64,
amd64 and the other for Microsoft.vc80.crt. Each have some issue.

-JC

David Gravereaux

unread,
Apr 19, 2007, 8:59:05 AM4/19/07
to
JesusCh...@gmail.com wrote:

> By any chance when you build your DLL, did it depend on msvcr80.dll?

No, I get msvcrt.dll. That's your compiler's fault. Replace -MD with -MT in the
command line and it should lose that dependency.

> Once I copy both jnior300.dll and the msvcr80.dll to the same
> directory as jnior310tcl2.dll the error message of "couldn't load
> library ....: this library or a dependent library could not be found
> in library path" went away but I got a new error message "could not
> find the section that owns the import directory".

That's a new one for me. I've never seen that before.

> When I look at the
> msvcr80.dll with "depends" it is a shade of red; with some functions
> green and others red. I have three of these DLLs: One for win64,
> amd64 and the other for Microsoft.vc80.crt. Each have some issue.

No clue.. all new.

--
"The intrepid Spaceman Spiff is stranded on a distant planet!
..our hero ruefully acknowledges that this happens fairly
frequently.." --- Calvin and Hobbes


signature.asc

JesusCh...@gmail.com

unread,
Apr 20, 2007, 4:36:03 AM4/20/07
to
On Apr 19, 5:59 am, David Gravereaux <davyg...@pobox.com> wrote:
> signature.asc
> 1KDownload

Well I guess my post at 7pm did go through. This is the 2nd time that
happens.


Well, the "-MT" did remove the dependency on the msvcr80.dll. I do
get a warning message:
LINK : warning LNK4098: defaultlib 'MSVCRT' conflicts with use of
other libs; use /NODEFAULTLIB:library
But it appears I can ignore it for now. When I looked at the contents
of the new DLL via "depends" it showed that the name of the function
is screwed up. I reviewed the code and remember I need to use extern
"C".

Therefore line:
__declspec(dllexport) Tcl_AppInitProc Jnior_Init;
Became line:
extern "C"
{
__declspec(dllexport) Tcl_AppInitProc Jnior_Init;
}

Question: Why can't the name be Jnior310tcl_Init? I tried that and
TCL crashed because it is expecting the name to be Jnior_Init.

Anyways, I'm able to connect to the device, but I'm unable to
disconnect from it. When I pass the handler to disconnect, I get an
error message:

C:\Program Files\TCL_TK\tcl_c_api2>tclsh
% load jnior310tcl2.dll
% set a [ jnior::Connect "192.168.5.2" 9200 "jnior" "jnior" ]
10001
% puts ${a}
10001
% jnior::Disconnect ${a}
Error 10004Error 115%
% exit
DLL Being unloaded now
C:\Program Files\TCL_TK\tcl_c_api2>

I even tried hardcoding the value, but nothing. Any ideas?

-JC

JesusCh...@gmail.com

unread,
Apr 20, 2007, 5:01:48 AM4/20/07
to

BTW: I added another function to tell the status time:
long __stdcall GetConnectionStatus(long handle, char
*TimeAtCurrentStatus);

When I pass the handler, it does return the time as expected. I feel
this may be an issue with the vendor.

C:\Program Files\TCL_TK\tcl_c_api2>tclsh
% load jnior310tcl2.dll
% set a [ jnior::Connect "192.168.5.2" 9200 "jnior" "jnior" ]
10001

% jnior::GetConnectionStatus ${a}
Total: 0 hours, 0 minutes, 8 seconds
% jnior::GetConnectionStatus ${a}
Total: 0 hours, 0 minutes, 15 seconds
% jnior::GetConnectionStatus ${a}
Total: 0 hours, 0 minutes, 24 seconds
% jnior::GetConnectionStatus ${a}
Total: 0 hours, 2 minutes, 32 seconds
% jnior::GetConnectionStatus ${a}
Total: 0 hours, 4 minutes, 38 seconds
% jnior::GetConnectionStatus ${a}
Total: 0 hours, 7 minutes, 23 seconds


% jnior::Disconnect ${a}

Error 115Error 10004%
%

-JC

Neil Madden

unread,
Apr 20, 2007, 11:10:01 AM4/20/07
to
JesusCh...@gmail.com wrote:
...

> Question: Why can't the name be Jnior310tcl_Init? I tried that and
> TCL crashed because it is expecting the name to be Jnior_Init.

It can. "Jnior_Init" is just Tcl's guess at what the routine would be
called (it's assuming that "310" is some version number), but you can
tell it instead:

load ./jnior310tcl2.dll Jnior310tcl

That should result in a call to Jnior310tcl_Init.

>
> Anyways, I'm able to connect to the device, but I'm unable to
> disconnect from it. When I pass the handler to disconnect, I get an
> error message:
>
> C:\Program Files\TCL_TK\tcl_c_api2>tclsh
> % load jnior310tcl2.dll
> % set a [ jnior::Connect "192.168.5.2" 9200 "jnior" "jnior" ]
> 10001
> % puts ${a}
> 10001
> % jnior::Disconnect ${a}
> Error 10004Error 115%
> % exit
> DLL Being unloaded now
> C:\Program Files\TCL_TK\tcl_c_api2>
>
> I even tried hardcoding the value, but nothing. Any ideas?

Looks like an error in the API you are using (or your use of it), rather
than a Tcl-related error.

PS - you know you can just use $a rather than ${a}?

-- Neil

David Gravereaux

unread,
Apr 20, 2007, 3:36:39 PM4/20/07
to
Neil Madden wrote:
> JesusCh...@gmail.com wrote:

>> Anyways, I'm able to connect to the device, but I'm unable to
>> disconnect from it. When I pass the handler to disconnect, I get an
>> error message:
>>
>> C:\Program Files\TCL_TK\tcl_c_api2>tclsh
>> % load jnior310tcl2.dll
>> % set a [ jnior::Connect "192.168.5.2" 9200 "jnior" "jnior" ]
>> 10001
>> % puts ${a}
>> 10001
>> % jnior::Disconnect ${a}
>> Error 10004Error 115%
>> % exit
>> DLL Being unloaded now
>> C:\Program Files\TCL_TK\tcl_c_api2>
>>
>> I even tried hardcoding the value, but nothing. Any ideas?
>
> Looks like an error in the API you are using (or your use of it), rather
> than a Tcl-related error.

Is there a document the explains the API or working C/C++ examples for it? The
part I question is the return code from Connect(). Does zero mean there was no
error? I bet there's some answers in the docs.

And who is printing those error messages to stdout? Is that you (in your code) or
is it the DLL doing it? The SOP manner of returning errors to Tcl from foreign
APIs is to use Tcl_SetErrorCode() so you get a result like the following:

% catch {::tiepie::GetSerialNumber}
1
^-- the call returned an error


% set errorInfo
Hardware is not connected
invoked from within
"::tiepie::GetSerialNumber"

^-- error message was set in the interp's result


% set errorCode
TIEPIE E_NO_HARDWARE {Hardware is not connected}

^-- error information contains the owner or source of the error as the first
element, in my case the TiePie API, a machine readable code, and the human
readable description.

It enables you to write code like the following:

proc MakeConnection {ip port user pass handleVar} {
global errorCode
upvar $handleVar handle

if {[catch {jnior::Connect $ip $port $user $pass} handle]} {
switch -- [lindex $errorCode 1] {
ETIMEOUT {}
EINVALIDPASSWD {}
EREFUSED {}
}
return 1
}
return 0
}

The switch cases with the E* names are the machine codes and within those blocks
you process for how you want the error to be used within your script.

--
Well, it just seemed wrong to cheat on an ethics test. -- Calvin

signature.asc

JesusCh...@gmail.com

unread,
Apr 20, 2007, 5:51:34 PM4/20/07
to
On Apr 20, 12:36 pm, David Gravereaux <davyg...@pobox.com> wrote:
> Neil Madden wrote:
> signature.asc
> 1KDownload

i did take a look at the documentation and their example is quit
simple:

result = Disconnect (jhandle);
if (result == 0)
cout << "\nDisconnected!" << endl;
else
{
cout << "Failed to Disconnect!" << endl;
return 1;
}


Is pretty much what I have. I do have a list of error codes, which is
from 100 to 114. The error code appears to be 115. But here is the
interesting thing. Under WISH, it works find. Like if WISH is
expecting an event of closure, where as TCLSH is not. I'll further
investigated.


-JC

David Gravereaux

unread,
Apr 20, 2007, 6:25:50 PM4/20/07
to
JesusCh...@gmail.com wrote:

....

> I do have a list of error codes, which is
> from 100 to 114. The error code appears to be 115. But here is the
> interesting thing. Under WISH, it works find. Like if WISH is
> expecting an event of closure, where as TCLSH is not. I'll further
> investigated.

Ohh.. This is where things become interesting regarding how execution is managed.
Normally (or I should say badly assumed to be normal), a call to one of the API
function comes from say a button press event in a windows GUI. The windows app is
processing windows messages in the standard loop:

WinMain (...) {
InitApp();
while (GetMessage(...) => 0) {
TranslateMsg(..);
DispatchMsg(...);
}
return 0;
}

If during one of those API calls, the API uses CreateWindow() to make a background
processing HWND to manage socket messages, the application's message loop will
service it as it was created in the loops own thread.

Now if you're using tclsh, there is no such process loop by default unless you
call [update] or [vwait]. In wish, the event loop is running by default.

Are there functions in the library for manually running event processing, or is it
assumed there is a running windows message pump?

--
I have plenty of common sense, I just choose to ignore it.
--- Calvin

signature.asc

JesusCh...@gmail.com

unread,
Apr 21, 2007, 1:35:47 AM4/21/07
to
On Apr 20, 3:25 pm, David Gravereaux <davyg...@pobox.com> wrote:
> signature.asc
> 1KDownload


> Are there functions in the library for manually running event processing, or is it
> assumed there is a running windows message pump?


I haven't study every function yet, since the past couple of weeks
where spent to port the DLL (in whole or in part) to TCL. I know
there exist four callback functions which I not sure how they'll turn
out. But this may answer your question, this device is basically a
PLC, with 8 digital inputs and 8 relay switches. I was told that the
instance the PLC receives a TRUE digital input it sends a signal to
what ever device is monitoring the status. I'm not sure if this is
true, it may require someone to constantly query the input ports. But
I'm not there yet. My task was to port the DLL to TCL. So someone or
myself can build the application on top of it. Which also, means that
porting the DLL may require more funcitons for TCL/TK, due to
requirement changes.


-JC

David Gravereaux

unread,
Apr 13, 2007, 3:19:36 PM4/13/07
to
JesusCh...@gmail.com wrote:
> I have a vendor (INTEG PG) that provides an SDK that includes
> a .dll, .h and .lib file. Since I haven't program in C for over ten
> years, I looked for a solution to repackage the DLL to be TCL
> friendly.

....

> Example: jnior_300.h has the following prototypes


>
> long __stdcall Connect(char *ip, unsigned short port, long *handle,
> char *user_name, char *password);

> long __stdcall Disconnect(long handle);

....

Let's hand roll those two. I'll assume the return code is an error code, with
zero being success and *handle is set by the function on return. The commands in
the interp will be [jnior::Connect <ip> <port> <user> <pass>], it returns the
handle of the connection. [jnior::Disconnect <handle>] will do the disconnect
called with the handle returned from Connect.

command to compile would be something like this:

cl -nologo -IC:\tcl\include jnior_tcl.c -link -dll -out:jniortcl.dll


-libpath:C:\Tcl\lib jnior300.lib

--- jnior_tcl.c -----

#define LEAN_AND_MEAN

MAKECMD(Connect);
MAKECMD(Disconnect);

--
Hobbes : What if the public doesn't like your work?
Calvin : They are not supposed to like it. This is avant-garde stuff!
I'm criticizing the low brows who can't appreciate great art
like this!

signature.asc
0 new messages