1 |
/* Tera Term extension mechanism |
2 |
Robert O'Callahan (roc+tt@cs.cmu.edu) |
3 |
|
4 |
Tera Term by Takashi Teranishi (teranishi@rikaxp.riken.go.jp) |
5 |
*/ |
6 |
|
7 |
/* HOW TO WRITE A TERATERM EXTENSION |
8 |
|
9 |
First of all, you will need the source code to Tera Term. For that, you |
10 |
should visit the Tera Term Web page: |
11 |
http://www.vector.co.jp/authors/VA002416/teraterm.html |
12 |
You will frequently need to refer to the source code to find out how things |
13 |
work and when your functions are called. However, please try to write your |
14 |
extension without assuming too much about the behaviour of Tera Term, in |
15 |
case it changes in the future! |
16 |
|
17 |
You will also need a compiler that can build Tera Term. So far, the |
18 |
extension system has only been tested with Visual C++ 4.2. Please report |
19 |
any problems to me (roc). |
20 |
|
21 |
Then you can compile this sample extension. |
22 |
|
23 |
Make sure you set the structure alignment option in the project to |
24 |
8 bytes (for Win32) or 2 bytes (for Win16), to be compatible with the |
25 |
standard Tera Term binary. |
26 |
|
27 |
You must add the Tera Term "common" directory to your include path |
28 |
to find the following 3 include files: |
29 |
*/ |
30 |
|
31 |
#include "teraterm.h" |
32 |
#include "tttypes.h" |
33 |
#include "ttplugin.h" |
34 |
|
35 |
/* These are the standard libraries used below. The main Tera Term program and |
36 |
all its DLLs are each statically linked to the C runtime library --- i.e. |
37 |
they do not require any runtime library DLL, and they each have private |
38 |
copies of the runtime library functions that they use. Therefore, it's |
39 |
probably best if TTXs use the same strategy. |
40 |
*/ |
41 |
|
42 |
#include <stdlib.h> |
43 |
#include <stdio.h> |
44 |
#include <string.h> |
45 |
|
46 |
#include "compat_w95.h" |
47 |
|
48 |
/* When you build this extension, it should be called TTXTEST.DLL. To try it |
49 |
out, copy it into the directory containing Tera Term. Currently, in order |
50 |
to use extensions with Tera Term, you have to set the environment variable |
51 |
TERATERM_EXTENSIONS to something. So in a command shell, use |
52 |
"set TERATERM_EXTENSIONS=1". Then use "ttermpro > dump" to run Tera Term |
53 |
and save the debugging output below to the file "dump". |
54 |
|
55 |
When TERATERM_EXTENSIONS is set, Tera Term automatically scans the |
56 |
directory containing it, looking for files of the form TTX*.DLL. |
57 |
It loads any that it finds. For each one that it finds, it calls TTXBind; |
58 |
see below for details. |
59 |
*/ |
60 |
|
61 |
|
62 |
/* This variable is used for the load order of the extension (see below for |
63 |
details). We also print it out in all the diagnostics, to make sure the |
64 |
functions are being called according to the correct order. */ |
65 |
#define ORDER 4000 |
66 |
|
67 |
/* This code demonstrates how to maintain separate instance variables for |
68 |
each instance of Tera Term that's using the DLL. It's easy in Win32, |
69 |
because it happens automatically, but in Win16 there is only one set of |
70 |
global data for all Tera Terms using the DLL, so we have to jump through |
71 |
some hoops. |
72 |
*/ |
73 |
static HANDLE hInst; /* Instance handle of TTX*.DLL */ |
74 |
|
75 |
typedef struct { |
76 |
PTTSet ts; |
77 |
PComVar cv; |
78 |
HMENU SetupMenu; |
79 |
} TInstVar; |
80 |
|
81 |
static TInstVar FAR * pvar; |
82 |
|
83 |
/* WIN32 allows multiple instances of a DLL */ |
84 |
static TInstVar InstVar; |
85 |
|
86 |
/* When this function is called, you should save copies of the ts and cv |
87 |
pointers if you need to access them later. All sorts of global session data |
88 |
is stored in these variables, and you can do all sorts of tricks by reading |
89 |
and modifying their fields in some of the functions below. You'll have to |
90 |
look at the Tera Term source code to see what the fields do and how and |
91 |
when they're used. |
92 |
|
93 |
This is called when Tera Term starts up, so don't do too much work in here |
94 |
or you will slow down the startup process even if your extension is not |
95 |
going to be used. |
96 |
*/ |
97 |
static void PASCAL FAR TTXInit(PTTSet ts, PComVar cv) { |
98 |
pvar->ts = ts; |
99 |
pvar->cv = cv; |
100 |
} |
101 |
|
102 |
/* This function is called when Tera Term is opening a TCP connection, before |
103 |
any Winsock functions have actually been called. |
104 |
|
105 |
You receive a 'hooks' structure containing pointers to pointers to all the |
106 |
Winsock functions that Tera Term will use. You can replace any of these |
107 |
function pointers to point to your own routines. However, if you replace a |
108 |
function pointer, you should save the old pointer somewhere and use that |
109 |
instead of calling Winsock directly. Some other extension might have put |
110 |
something there for you to use! |
111 |
|
112 |
For example: |
113 |
|
114 |
[in TTXOpenTCP] |
115 |
... saved_connect = hooks->Pconnect; hooks->Pconnect = my_connect; ... |
116 |
|
117 |
[in my_connect] |
118 |
... saved_connect(...); ... |
119 |
[don't call the real connect!] |
120 |
|
121 |
Extensions that don't apply to all sessions (e.g. any extension that can be |
122 |
disabled by the user) will check to see if they apply to the current |
123 |
session, and if they don't, then no functions are be changed. |
124 |
|
125 |
This function is called for each extension, in load order (see below). |
126 |
Thus, the extension with highest load order puts its hooks in last. |
127 |
*/ |
128 |
static void PASCAL FAR TTXOpenTCP(TTXSockHooks FAR * hooks) { |
129 |
printf("TTXOpenTCP %d\n", ORDER); |
130 |
} |
131 |
|
132 |
/* This function is called when Tera Term is closing a TCP connection, after |
133 |
all Winsock functions have been called. |
134 |
|
135 |
Here you should restore any hooked pointers that you saved away. For |
136 |
example: |
137 |
... hooks->Pconnect = saved_connect; ... |
138 |
|
139 |
This function is called for each extension, in reverse load order (see |
140 |
below). |
141 |
*/ |
142 |
static void PASCAL FAR TTXCloseTCP(TTXSockHooks FAR * hooks) { |
143 |
printf("TTXCloseTCP %d\n", ORDER); |
144 |
} |
145 |
|
146 |
/* This function is called when Tera Term has loaded the TTDLG library. It |
147 |
gives the extension an opportunity to modify the function pointers that are |
148 |
used to create dialog boxes. |
149 |
|
150 |
Unlike the TCP functions, there's no reason to call the previous version of |
151 |
a hooked function. When you replace a dialog box, the new dialog box should |
152 |
do everything that the original dialog box did, plus any extra controls |
153 |
that you want. You'll probably have to copy the code from TTDLG to do this. |
154 |
|
155 |
If multiple extensions want to replace the same dialog box, the one with |
156 |
the highest load order number (see below) wins. For this reason, when |
157 |
writing an extension, you should look at the extensions that already exist |
158 |
and try to figure out how to make your extension work nicely with the |
159 |
others that modify the same dialog box as you do. I suggest that anyone who |
160 |
modifies a dialog box should export functions from their extension DLL so |
161 |
that other code can change the extra settings without going through that |
162 |
dialog box. Then, if there is an extension with a lower load order number |
163 |
that changes a dialog box that you also want to change, you can create a |
164 |
new dialog box that has all their changes plus your own, and call the |
165 |
exported functions in the other DLL when the user sets options for that |
166 |
other DLL. If that didn't make sense, send me email :-). |
167 |
|
168 |
A typical use for this function is to hook the GetHostName function so that |
169 |
extra options can be added to the connection dialog box. Whenever possible |
170 |
(without making the user interface clumsy), an extension should introduce |
171 |
new dialog boxes reachable by adding menu items (see below), rather than |
172 |
overriding an existing dialog box. Any UI for setup options should probably |
173 |
be handled this way. |
174 |
|
175 |
This function is called for each extension, in load order (see below). |
176 |
Thus, the extension with highest load order puts its hooks in last. |
177 |
*/ |
178 |
static void PASCAL FAR TTXGetUIHooks(TTXUIHooks FAR * hooks) { |
179 |
printf("TTXSetUIHooks %d\n", ORDER); |
180 |
} |
181 |
|
182 |
/* This function is called when Tera Term has loaded the TTSETUP library. It |
183 |
gives the extension an opportunity to modify the function pointers that are |
184 |
used to read and write the settings file (TERATERM.INI) and to read and |
185 |
write the command line. |
186 |
|
187 |
An extension will almost always hook these functions. When Tera Term reads |
188 |
or writes its setup file, the extension will read or write its own internal |
189 |
settings to an appropriate section of the INI file. When Tera Term parses |
190 |
its command line, or builds a new command line to spawn a new Tera Term |
191 |
session, the extension will check for its own options or write out its own |
192 |
options. |
193 |
|
194 |
Any hooked functions should pass through to the old functions, just as in |
195 |
TTXOpenTCP. |
196 |
|
197 |
This function is called for each extension, in load order (see below). |
198 |
Thus, the extension with highest load order puts its hooks in last. |
199 |
*/ |
200 |
static void PASCAL FAR TTXGetSetupHooks(TTXSetupHooks FAR * hooks) { |
201 |
printf("TTXSetSetupHooks %d\n", ORDER); |
202 |
} |
203 |
|
204 |
/* This function is called whenever Tera Term changes the window size. |
205 |
|
206 |
This function is called for each extension, in load order (see below). |
207 |
*/ |
208 |
static void PASCAL FAR TTXSetWinSize(int rows, int cols) { |
209 |
printf("TTXSetWinSize %d\n", ORDER); |
210 |
} |
211 |
|
212 |
/* This function is called when Tera Term creates a new menu. This can happen |
213 |
quite often, especially when the menubar is hidden and Tera Term wants to |
214 |
create a popup menu because the user ctrl-clicked in the window. |
215 |
|
216 |
The 'menu' parameter is the HMENU for the menu bar. The extension can add |
217 |
items to the existing submenus or even add an entirely new submenu. This is |
218 |
great for adding menu items that control the extension's options. |
219 |
|
220 |
This function is called for each extension, in load order (see below). |
221 |
Thus, the extension with highest load order number puts its items in last. |
222 |
*/ |
223 |
#define ID_MENUITEM 6000 |
224 |
static void PASCAL FAR TTXModifyMenu(HMENU menu) { |
225 |
UINT flag = MF_ENABLED; |
226 |
|
227 |
printf("TTXModifyMenu %d\n", ORDER); |
228 |
|
229 |
pvar->SetupMenu = GetSubMenu(menu,2); |
230 |
AppendMenu(pvar->SetupMenu,MF_SEPARATOR,0,NULL); |
231 |
if (pvar->ts->Debug>0) flag |= MF_CHECKED; |
232 |
AppendMenu(pvar->SetupMenu,flag, ID_MENUITEM,"&Debug mode"); |
233 |
} |
234 |
|
235 |
/* This function is called when Tera Term pops up a submenu menu. |
236 |
|
237 |
The 'menu' parameter is the HMENU for the submenu. The extension can change |
238 |
the status of any of the menu items, for example graying out some items. |
239 |
Extensions should make sure that this is actually the submenu that they |
240 |
care about! |
241 |
|
242 |
This function is called for each extension, in load order (see below). |
243 |
*/ |
244 |
static void PASCAL FAR TTXModifyPopupMenu(HMENU menu) { |
245 |
printf("TTXModifyPopupMenu %d\n", ORDER); |
246 |
|
247 |
if (menu==pvar->SetupMenu) |
248 |
{ |
249 |
if (pvar->cv->Ready) |
250 |
EnableMenuItem(pvar->SetupMenu,ID_MENUITEM,MF_BYCOMMAND | MF_ENABLED); |
251 |
else |
252 |
EnableMenuItem(pvar->SetupMenu,ID_MENUITEM,MF_BYCOMMAND | MF_GRAYED); |
253 |
} |
254 |
} |
255 |
|
256 |
/* This function is called when Tera Term receives a command message. |
257 |
|
258 |
The extension returns 1 if it processed the message, 0 otherwise. If it |
259 |
says it processed the message, then the message will not be processed |
260 |
anywhere else. The extensions look at the messages before the main |
261 |
Tera Term code, so be careful of overriding existing commands. |
262 |
|
263 |
This function is called for each extension, in reverse load order (see |
264 |
below). Thus, the extension that has highest load order number gets to |
265 |
process the command first. |
266 |
*/ |
267 |
static int PASCAL FAR TTXProcessCommand(HWND hWin, WORD cmd) { |
268 |
printf("TTXProcessCommand %d\n", ORDER); |
269 |
|
270 |
if (cmd==ID_MENUITEM) |
271 |
{ |
272 |
if (pvar->ts->Debug==0) |
273 |
{ |
274 |
pvar->ts->Debug=1; |
275 |
CheckMenuItem(pvar->SetupMenu,ID_MENUITEM,MF_BYCOMMAND | MF_CHECKED); |
276 |
} |
277 |
else { |
278 |
pvar->ts->Debug=0; |
279 |
CheckMenuItem(pvar->SetupMenu,ID_MENUITEM,MF_BYCOMMAND | MF_UNCHECKED); |
280 |
} |
281 |
return 1; |
282 |
} |
283 |
return 0; |
284 |
} |
285 |
|
286 |
/* This function is called when Tera Term is quitting. You can use it to clean |
287 |
up. |
288 |
|
289 |
This function is called for each extension, in reverse load order (see |
290 |
below). |
291 |
*/ |
292 |
static void PASCAL FAR TTXEnd(void) { |
293 |
printf("TTXEnd %d\n", ORDER); |
294 |
} |
295 |
|
296 |
/* |
297 |
*/ |
298 |
static void PASCAL FAR TTXSetCommandLine(PCHAR cmd, int cmdlen, PGetHNRec rec) { |
299 |
printf("TTXSetCommandLine %d\n", ORDER); |
300 |
} |
301 |
|
302 |
/* This function is called when Tera Term is opening a Serial port and Replay |
303 |
log file. |
304 |
|
305 |
You receive a 'hooks' structure containing pointers to pointers to all the |
306 |
file IO functions that Tera Term will use. You can replace any of these |
307 |
function pointers to point to your own routines. However, if you replace a |
308 |
function pointer, you should save the old pointer somewhere and use that |
309 |
instead of calling file IO functions directly. Some other extension might |
310 |
have put something there for you to use! |
311 |
|
312 |
For example: |
313 |
|
314 |
[in TTXOpenFile] |
315 |
... saved_createfile = hooks->PCreateFile; hooks->PCreateFile = my_createfile; ... |
316 |
|
317 |
[in my_createfile] |
318 |
... saved_createfile(...); ... |
319 |
[don't call the real CreateFile!] |
320 |
|
321 |
Extensions that don't apply to all sessions (e.g. any extension that can be |
322 |
disabled by the user) will check to see if they apply to the current |
323 |
session, and if they don't, then no functions are be changed. |
324 |
|
325 |
This function is called for each extension, in load order (see below). |
326 |
Thus, the extension with highest load order puts its hooks in last. |
327 |
*/ |
328 |
static void PASCAL FAR TTXOpenFile(TTXFileHooks FAR * hooks) { |
329 |
printf("TTXOpenFile %d\n", ORDER); |
330 |
} |
331 |
|
332 |
/* This function is called when Tera Term is closing a serial connection, |
333 |
after all file IO functions have been called. |
334 |
|
335 |
Here you should restore any hooked pointers that you saved away. For |
336 |
example: |
337 |
... hooks->PCreateFile = saved_createfile; ... |
338 |
|
339 |
This function is called for each extension, in reverse load order (see |
340 |
below). |
341 |
*/ |
342 |
static void PASCAL FAR TTXCloseFile(TTXFileHooks FAR * hooks) { |
343 |
printf("TTXCloseFile %d\n", ORDER); |
344 |
} |
345 |
|
346 |
/* This record contains all the information that the extension forwards to the |
347 |
main Tera Term code. It mostly consists of pointers to the above functions. |
348 |
Any of the function pointers can be replaced with NULL, in which case |
349 |
Tera Term will just ignore that function and assume default behaviour, |
350 |
which means "do nothing". |
351 |
*/ |
352 |
static TTXExports Exports = { |
353 |
/* This must contain the size of the structure. See below for its usage. */ |
354 |
sizeof(TTXExports), |
355 |
|
356 |
/* This is the load order number of this DLL. It affects which order the above |
357 |
functions are called in, as noted for each function. Choose this number |
358 |
carefully! Typically the DLLs with higher numbers are layered on top of the |
359 |
DLLs with lower numbers. Thus, a DLL that does SOCKS redirection will have |
360 |
a lower load order than a DLL that does the SSH protocol, because SOCKS |
361 |
redirection must happen "before" SSH processing. Likewise, a DLL that does |
362 |
Kerberos authentication must have a higher order number than a DLL that |
363 |
does SSL, because the Kerberos protocol uses telnet options that would go |
364 |
"on top of" SSL. |
365 |
|
366 |
Currently, no order numbers are used because no real extensions have been |
367 |
written. |
368 |
I suggest the following numbers: |
369 |
0-999: Basic network naming and communication (e.g. SOCKS) |
370 |
1000-1999: Transport emulation (e.g. SSL) |
371 |
2000-2999: Protocols (e.g. SSH, telnet) |
372 |
3000-3999: Protocol extensions (e.g. Kerberos telnet options) |
373 |
4000-4999: Application features (e.g. file transfers, UI for hidden setup |
374 |
options) |
375 |
|
376 |
Try to use numbers in the middle of any available range so that other |
377 |
extensions can load before or after you as they wish. |
378 |
*/ |
379 |
ORDER, |
380 |
|
381 |
/* Now we just list the functions that we've implemented. */ |
382 |
TTXInit, |
383 |
TTXGetUIHooks, |
384 |
TTXGetSetupHooks, |
385 |
TTXOpenTCP, |
386 |
TTXCloseTCP, |
387 |
TTXSetWinSize, |
388 |
TTXModifyMenu, |
389 |
TTXModifyPopupMenu, |
390 |
TTXProcessCommand, |
391 |
TTXEnd, |
392 |
TTXSetCommandLine, |
393 |
TTXOpenFile, |
394 |
TTXCloseFile |
395 |
}; |
396 |
|
397 |
/* This is the function that Tera Term calls to retrieve the export |
398 |
information. This code is for Visual C++. The name that gets exported is |
399 |
"_TTXBind@8" and that is what the Tera Term program looks for. So, |
400 |
whichever compiler you use, you must make sure that that name is exported. |
401 |
|
402 |
The job of this function is to copy the export data from the structure |
403 |
above into the record that Tera Term passed us a pointer to. That record |
404 |
contains its size; we make sure we don't copy more than that amount of |
405 |
data. In the future, if we add TTX functions to the TTXExports record, |
406 |
then we could have new extensions that have a bigger TTXExports record than |
407 |
old Tera Term binaries. In this case, the extra functions will simply not |
408 |
be called. This means we can write extensions that will work with both old |
409 |
and new versions of Tera Term. |
410 |
|
411 |
(In a similar way, we can run old extensions with new versions of Tera Term. |
412 |
The main program initialises its exports record to zeroes before it calls |
413 |
TTXBind. This means that any data we don't copy in there is NULL, so any |
414 |
extra functions that have been added since this extension was compiled |
415 |
will automatically be NULL and thus get default behaviour.) |
416 |
*/ |
417 |
BOOL __declspec(dllexport) PASCAL FAR TTXBind(WORD Version, TTXExports FAR * exports) { |
418 |
int size = sizeof(Exports) - sizeof(exports->size); |
419 |
/* do version checking if necessary */ |
420 |
/* if (Version!=TTVERSION) return FALSE; */ |
421 |
|
422 |
if (size > exports->size) { |
423 |
size = exports->size; |
424 |
} |
425 |
memcpy((char FAR *)exports + sizeof(exports->size), |
426 |
(char FAR *)&Exports + sizeof(exports->size), |
427 |
size); |
428 |
return TRUE; |
429 |
} |
430 |
|
431 |
BOOL WINAPI DllMain(HANDLE hInstance, |
432 |
ULONG ul_reason_for_call, |
433 |
LPVOID lpReserved) |
434 |
{ |
435 |
switch( ul_reason_for_call ) { |
436 |
case DLL_THREAD_ATTACH: |
437 |
/* do thread initialization */ |
438 |
break; |
439 |
case DLL_THREAD_DETACH: |
440 |
/* do thread cleanup */ |
441 |
break; |
442 |
case DLL_PROCESS_ATTACH: |
443 |
/* do process initialization */ |
444 |
DoCover_IsDebuggerPresent(); |
445 |
hInst = hInstance; |
446 |
pvar = &InstVar; |
447 |
break; |
448 |
case DLL_PROCESS_DETACH: |
449 |
/* do process cleanup */ |
450 |
break; |
451 |
} |
452 |
return TRUE; |
453 |
} |