home *** CD-ROM | disk | FTP | other *** search
/ Arawak OS/2 Shareware / PAKLED.ISO / docs / edm / edmi1-7.inf (.txt) < prev    next >
Encoding:
OS/2 Help File  |  1993-12-08  |  148.6 KB  |  2,865 lines

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Title Page ΓòÉΓòÉΓòÉ
  3.  
  4.            Welcome to EDM/2 - The Electronic OS/2 Developers Magazine!
  5.                    Portions copyright (c) by Larry Salomon Jr.
  6.                                 Volume 1, issue 7
  7.  
  8. Copyright Notice and Other Stuff 
  9.  
  10. The editor of this electronic magazine is Larry Salomon, Jr. 
  11.  
  12. Portions of EDM/2 are copyrighted by the editors.  This publication may be 
  13. freely distributed in electronic form provided that all parts are present in 
  14. their original unmodified form.  A reasonable fee may be charged for the 
  15. physical act of distribution; no fee may be charged for the publication itself. 
  16.  
  17. All articles are copyrighted by their authors. No part of any article may be 
  18. reproduced without permission from the original author. 
  19.  
  20. Neither this publication nor the editors are affiliated with International 
  21. Business Machines Corporation. 
  22.  
  23. OS/2 is a registered trademark of International Business Machines Corporation. 
  24. Other trademarks are property of their respective owners.  Any mention of a 
  25. product in this publication does not constitute an endorsement or affiliation 
  26. unless specifically stated in the text. 
  27.  
  28. Administrivia 
  29.  
  30. Yes, the mailing distribution was goofed up.  Many of you received parts 1 and 
  31. 4 only.  This is because the mailer runs Sendmail in the background but forgets 
  32. to wait for it to complete before deleting the files it is supposed to be 
  33. mailing.  Oops.  My apologies for the inconvenience. 
  34.  
  35. The fix to the problem has been volunteered by Gess Shankar, who runs the OS/2 
  36. Multimedia Mailing List.  He offered to run the mailing distribution using his 
  37. tried-and-true mailing list machine.  This will allow for much more robust 
  38. subscription processing as well as the ability to request back issues; those of 
  39. you who are currently subscribed via the "old way" have already been moved to 
  40. the new machine, so there is no need to resubscribe.  To receive more 
  41. information (for new readers), send an empty mail message to 
  42. EDM2@knex.via.mind.org and you will receive the information file that tell you 
  43. how to subscribe, etc. 
  44.  
  45. I still have not heard from Gavin Baker so I will assume him to be "missing in 
  46. action"; because of this I will be writing the "Introduction to Presentation 
  47. Manager Programming" column and will start over from the beginning.  Speaking 
  48. of people missing in action, I received a note from Steve (Luzynski).  He is 
  49. doing well and now has a full-time job as a computer retail salesperson which 
  50. leaves him with "no free time."  I guess that answers the question I had about 
  51. really being the editor or just being a stop-gap until he came back. 
  52.  
  53. By now, many if not all of you have heard of WordPerfect's announcement to halt 
  54. development on WordPerfect version 6.0 for OS/2.  Who knows what this portends? 
  55. I certainly do not, but I do know that I am glad I am not in Orem, Utah right 
  56. now...You are probably thinking, "what the hell does this have to do with OS/2 
  57. development."  Consider that a company of that size makes a decision like this 
  58. for one of three reasons: political alliances could indicate a conflict of 
  59. interest, marketing demand could indicate it is not worth the investment, or 
  60. development strategies in the environment could make the product obsolete in a 
  61. short time.  My hunch is that they know something we don't about IBM's 
  62. direction but we had better find out! 
  63.  
  64. There is both a correction and a clarification that are noteworthy in the 
  65. Scratch Patch column.  Also, we need  your submissions for the Snippet(s) of 
  66. the Month portion of that column!  Not only is group participation better, but 
  67. this old dog hasn't learned all of the possible tricks in OS/2 programming.  I 
  68. am always looking to grow, too! 
  69.  
  70. While talking to Dick Conklin a few days ago, he asked me to mention that you 
  71. might qualify for a free subscription to OS/2 Developer, another OS/2 magazine 
  72. aimed at developers.  Call 1-800-WANT-OS2 and ask them for the form to fill 
  73. out.  If you are a member of the Developers Assistance Program (DAP), be sure 
  74. to indicate this on the form to guarantee your acceptance. 
  75.  
  76. Some other tweaks on the magazine were made, notably, the icon file has been 
  77. renamed to EDMI.ICO and the installation program has been changed accordingly. 
  78. This will allow you to remove all of the previous icon files and only keep the 
  79. latest on your disk.  Also, an ASCII file containing the table of contents will 
  80. be distributed with each issue as CONTENTS.NDX to allow you to see the titles 
  81. of the articles for that month.  The contents of issues 1 through 6 can be 
  82. found in the Reader's Choice Awards in this issue and can also be found in the 
  83. .ZIP file as OLDNDXS.NDX  Many thanks to the readers who suggested these 
  84. changes. 
  85.  
  86. Finally, I knew this was going to happen and it did.  I forgot to add two 
  87. articles to this month's index - OS/2 Installable File Systems - Part 3 and 
  88. Using OS/2 2.x bitmap files.  Because of this, I am resending this; I would 
  89. hate for them to not be considered for the Reader's Choice Awards because of my 
  90. screw-up. 
  91.  
  92. Select this to go to the next section 
  93.  
  94.  
  95. ΓòÉΓòÉΓòÉ 2. This Issue's Features ΓòÉΓòÉΓòÉ
  96.  
  97. The following articles constitute this issue's features: 
  98.  
  99. o OS/2 Installable File Systems - Part 3 
  100. o Rexx-ercising Your Applications - Part 2 
  101. o Using OS/2 2.x bitmap files 
  102. o Writing a Direct Manipulation Spy 
  103.  
  104.  
  105. ΓòÉΓòÉΓòÉ 2.1. OS/2 Installable File Systems - Part 3 ΓòÉΓòÉΓòÉ
  106.  
  107. Written by Andre Asselin 
  108.  
  109. Introduction 
  110.  
  111. Whew!  Between my regular job (I don't call it my day job anymore, because it 
  112. seems like I've been spending more and more nights there too), this month's 
  113. code not working, Larry breathing down my neck for an article, and the joys of 
  114. Christmas shopping, keeping sane recently has not been easy.  I'll tell you, 
  115. Murphy was right - if it wasn't for the last minute, a lot of things wouldn't 
  116. get done. 
  117.  
  118. Last time, I said that I'd present a state diagram that showed how the 
  119. IFS/control program couldn't get deadlocked.  I'm afraid that'll have to wait 
  120. until next time.  The code was very uncooperative this month, and I ended up 
  121. not having enough time to finish it.  Instead, I concentrated on getting the 
  122. code into a real skeleton that you could just insert your own guts into, and 
  123. also started inserting some guts for our versioning file system.  In doing 
  124. that, I of course ran into some problems... 
  125.  
  126. But before I start my detective story, I'd like to make official what has been 
  127. happening for the past few months:  this series is officially bi-monthly.  As 
  128. much as I'd like to do a monthly series, I'm afraid I don't have the time.  As 
  129. always, though, if you have specific questions that you need answered before 
  130. this series gets to them, feel free to write, and I'll try my best to explain. 
  131.  
  132. Also, for those that don't know, I did an IPF version of the IFS document.  It 
  133. is available on Compuserve in the DAP section of the OS2DF2 forum (section 14), 
  134. and is called IFSDOC.ZIP.  You'll have to become a member of IBM's World Wide 
  135. DAP to get access to that section, but the good news is joining is free and 
  136. easy.  Just GO OS2DAP on Compuserve and answer the questions.  In about a week 
  137. you should receive a welcome letter and have access to that section. 
  138.  
  139. Select this to go to the next section 
  140.  
  141.  
  142. ΓòÉΓòÉΓòÉ 2.1.1. Introduction ΓòÉΓòÉΓòÉ
  143.  
  144. Written by Andre Asselin 
  145.  
  146. Whew!  Between my regular job (I don't call it my day job anymore, because it 
  147. seems like I've been spending more and more nights there too), this month's 
  148. code not working, Larry breathing down my neck for an article, and the joys of 
  149. Christmas shopping, keeping sane recently has not been easy.  I'll tell you, 
  150. Murphy was right - if it wasn't for the last minute, a lot of things wouldn't 
  151. get done. 
  152.  
  153. Last time, I said that I'd present a state diagram that showed how the 
  154. IFS/control program couldn't get deadlocked.  I'm afraid that'll have to wait 
  155. until next time.  The code was very uncooperative this month, and I ended up 
  156. not having enough time to finish it.  Instead, I concentrated on getting the 
  157. code into a real skeleton that you could just insert your own guts into, and 
  158. also started inserting some guts for our versioning file system.  In doing 
  159. that, I of course ran into some problems... 
  160.  
  161. But before I start my detective story, I'd like to make official what has been 
  162. happening for the past few months:  this series is officially bi-monthly.  As 
  163. much as I'd like to do a monthly series, I'm afraid I don't have the time.  As 
  164. always, though, if you have specific questions that you need answered before 
  165. this series gets to them, feel free to write, and I'll try my best to explain. 
  166.  
  167. Also, for those that don't know, I did an IPF version of the IFS document.  It 
  168. is available on Compuserve in the DAP section of the OS2DF2 forum (section 14), 
  169. and is called IFSDOC.ZIP.  You'll have to become a member of IBM's World Wide 
  170. DAP to get access to that section, but the good news is joining is free and 
  171. easy.  Just GO OS2DAP on Compuserve and answer the questions.  In about a week 
  172. you should receive a welcome letter and have access to that section. 
  173.  
  174. Select this to go to the next section 
  175.  
  176.  
  177. ΓòÉΓòÉΓòÉ 2.1.2. It used to work... ΓòÉΓòÉΓòÉ
  178.  
  179. The code I presented last month was the beginnings of a skeleton for a ring 
  180. 0/ring 3 IFS.  The design and concept were all hashed out, but the actual 
  181. skeleton code for all the ring 0 stubs and for the ring 3 router weren't 
  182. complete.  That was the first thing I worked on - filling in all the missing 
  183. skeleton code.  The first thing I had to do was expand the union in R0R3SHAR.H 
  184. that defines all the data passed between the IFS and control program to include 
  185. structures for all the FS_ calls.  Once that was done, the next thing to do was 
  186. cut and paste the code for the ring 0 FS_ATTACH into all the other FS_ 
  187. functions so that they would route their calls to the control program. 
  188. Finally, the control program's router had to have entries added for all the 
  189. other FS_ calls.  Talk about work that's hard to get psyched for! Cutting and 
  190. pasting is not my idea of a stimulating programming session. 
  191.  
  192. Once that was done, the next thing to do was to actually start filling in some 
  193. of those stubs.  The first one to do is FS_ATTACH, since that is the first call 
  194. this type of IFS will receive.  After that, a good second one is FS_CHDIR, 
  195. since directories are very basic to any IFS.  The code I filled in isn't all 
  196. that elaborate - just something to start testing with.  At that point, though I 
  197. decided it was time to give it a whirl and see how it ran.  After fixing a few 
  198. syntax errors, it was time for the big moment...and it didn't work anymore! 
  199.  
  200. OS/2 booted fine and loaded the IFS.  The banner came up, and the system didn't 
  201. crash.  Things were looking good until I ran the ring 3 control program, when 
  202. it failed trying to attach to the IFS.  Funny, that worked before.  Looking up 
  203. the error code revealed it to be ERROR_INVALID_PARAMETER. I figured it was 
  204. probably just a typo or something. 
  205.  
  206. A week later I still hadn't a clue what was causing the problem.  I narrowed it 
  207. down to a call to VMLock in the IFS, but all it's parameters looked good.  I 
  208. thought it might be that OS/2 had gone flaky on my system, so I tried it on 
  209. another system at work.  Same thing.  I thought that maybe my assembly DevHlp 
  210. functions were messing up, so I wrote direct calls to the DevHlps in the code. 
  211. Same thing.  I thought maybe the lock handle VMLock uses couldn't be in 
  212. swappable memory, so I put it in movable, and then fixed memory.  Same thing. 
  213. Nothing seemed to want to cure this problem. 
  214.  
  215. At that point I got the brilliant idea of taking the code from the last 
  216. article, compiling and running it, and seeing what changes I'd made. Well, I 
  217. found out much to my dismay, that the code from last month had the same 
  218. problem.  I considered how lucky I was that the flood of angry email letters 
  219. everyone sent me decrying my foul code must have somehow gotten filtered, and 
  220. never made it to my electronic mail box.  So after counting my blessings, I 
  221. decided to get a beer.  Not that that would help me find the problem, but it 
  222. would help me think up an excuse to tell Larry why my article would be late. 
  223.  
  224. Select this to go to the next section 
  225.  
  226.  
  227. ΓòÉΓòÉΓòÉ 2.1.3. The C preprocessor strikes again ΓòÉΓòÉΓòÉ
  228.  
  229. Funny thing about problems where you have no clue what the cause is - you kind 
  230. of want to hibernate for a month or two and hope somebody else fixes it. 
  231. Unfortunately among the list of things I have, a co-author is not one of them, 
  232. so I would have solve this one myself. 
  233.  
  234. I won't pretend that what I did next had any kind of methodical systematic 
  235. approach behind it.  My whole focus at that point was to just make the call 
  236. work somehow, so I inserted a return right after the call and started playing 
  237. with the parameters.  The first break came when I could make the call work by 
  238. doing two VMAlloc's before the call, one for the data block to be locked down, 
  239. and the other for the memory to put the lock handle into.  A little more 
  240. playing narrowed it down to just the VMAlloc for the lock handle.  If I kept 
  241. the call to VMLock the same as it was originally, but inserted that VMAlloc 
  242. before it, it worked fine.  Okay, so that pointed to a problem with the VMAlloc 
  243. for the lock handle, which was done in the initialization routine.  I thought 
  244. maybe it's DevHlp routine was messed up and took a look at it.  It looked fine. 
  245. I decided to take a long look at the flags again to see if there was something 
  246. I was missing, and while looking at the PDD reference, I decided to see if the 
  247. values for the flags in the .H file matched those in the book.  Lo and behold - 
  248. VMAF_GLOBALSPACE was defined the opposite of what it should have been.  The 
  249. initialization routine was allocating memory for the lock handles in memory 
  250. local to the process that calls the IFS at initialization time, instead of 
  251. globally.  VMLock had been complaining about not being able to address the lock 
  252. handle!  The whole problem was that I was looking at the code for the VMAlloc 
  253. which clearly included the flag to allocate global memory, but the flag value 
  254. was wrong.  Bitten again by the C preprocessor. 
  255.  
  256. Select this to go to the next section 
  257.  
  258.  
  259. ΓòÉΓòÉΓòÉ 2.1.4. Up and running, almost ΓòÉΓòÉΓòÉ
  260.  
  261. Once I got that bug figured out, I was finally back to where I was before. The 
  262. control program could load and attach to the IFS, and I could issue a 
  263. DosFSAttach()  call to associate a drive letter with the IFS.  Now was the big 
  264. chance to try and see if the new FS_CHDIR code would work.  I typed CD T:\1 and 
  265. got back an error code of ERROR_PATH_NOT_FOUND.  Hmmm.  That's strange.  When I 
  266. did the attach, I said for it to attach drive T to drive C, and I knew that 
  267. directory existed on drive C. Time to add debugging code.  At least since the 
  268. problem was in the ring 3 piece, I didn't have to reboot after every recompile. 
  269. Waiting for OS/2 to shutdown and restart every ten minutes was getting to be 
  270. tiring. 
  271.  
  272. After the debugging code was added, I found that the FS_CHDIR call was failing 
  273. because DosQueryPathInfo() said that the directory I was passing in wasn't a 
  274. directory.  Double hmmm.  I decided to have the control program print out what 
  275. attributes DosQueryPathInfo() thought my directory had.  It seems 
  276. DosQueryPathInfo() was being very generous and saying my directory had lots of 
  277. attributes, even some of the reserved ones, but not the directory attribute. 
  278. Running it with Turbo Debugger and looking at the structure showed the same 
  279. thing.  This was too weird.  I decided to pull the DosQueryPathInfo() call out 
  280. into a little test program and add some code to dump the raw data that 
  281. DosQueryPathInfo() returned.  The code looked like this: 
  282.  
  283. static FILESTATUS3 buf;
  284. int i;
  285.  
  286.   :
  287. for (i=0; i<sizeof(buf); i++) {
  288.    printf("%u ", ((unsigned char *)&buf)[i]);
  289. }
  290.  
  291. Now something else weird was happening - it was printing out too many bytes. 
  292. So I played with it a little, trying some variations before I just decided to 
  293. look at the data. 
  294.  
  295. Well, to understand the data, you have to know the structure, so I looked it up 
  296. in the Control Program reference.  A FILESTATUS3 has 3 FDATE and FTIME 
  297. structures at the head of the structure, followed by two fields related to the 
  298. file size, and finally the file attributes.  Looking up an FDATE in the book 
  299. said it consists of 3 USHORT's, one each for day, month, and year.  Okay, fine, 
  300. but now the dump code wasn't printing enough data.  Time to look at the header 
  301. files. 
  302.  
  303. Looking at the BSEDOS.H header file in the 2.1 Toolkit showed the following 
  304. definition for an FDATE (FTIME was similar): 
  305.  
  306. #ifdef __IBMC__
  307. typedef struct _FDATE           /* fdate */
  308. {
  309.    UINT   day     : 5;
  310.    UINT   month   : 4;
  311.    UINT   year    : 7;
  312. } FDATE;
  313. typedef FDATE   *PFDATE;
  314. #else
  315. typedef struct _FDATE           /* fdate */
  316. {
  317.    USHORT   day     : 5;
  318.    USHORT   month   : 4;
  319.    USHORT   year    : 7;
  320. } FDATE;
  321. typedef FDATE   *PFDATE;
  322. #endif
  323.  
  324. Now I see where the Control Program reference was wrong - it left off those 
  325. important little ':  5' things on the end.  An FDATE wasn't 3 USHORT's, it was 
  326. one!  After that discovery, I got to wondering what the definition was in the 
  327. header files Borland ships with it's compiler.  So I pulled up BSEDOS.H in the 
  328. bcos2\include directory, and found this: 
  329.  
  330. #if defined(__IBMC__) || defined(__BORLANDC__)
  331. typedef struct _FDATE           /* fdate */
  332. {
  333.    UINT   day     : 5;
  334.    UINT   month   : 4;
  335.    UINT   year    : 7;
  336. } FDATE;
  337. typedef FDATE   *PFDATE;
  338. #else
  339. typedef struct _FDATE           /* fdate */
  340. {
  341.    USHORT   day     : 5;
  342.    USHORT   month   : 4;
  343.    USHORT   year    : 7;
  344. } FDATE;
  345. typedef FDATE   *PFDATE;
  346. #endif
  347.  
  348. Well, guess what, a UINT is not the same size as a USHORT in 32-bit code!  It 
  349. seems someone at Borland forgot that. As a result, every structure that used an 
  350. FDATE or FTIME was messed up (including FILEFINDBUF3, which was what I was 
  351. trying to use).  Getting rid of the '||' and everything after it did the trick 
  352. - now DosQueryPathInfo() was working like it should. For your reference, lines 
  353. 399 and 418 in the BSEDOS.H that Borland ships need to have this fix applied. 
  354.  
  355. Select this to go to the next section 
  356.  
  357.  
  358. ΓòÉΓòÉΓòÉ 2.1.5. Debugging tools ΓòÉΓòÉΓòÉ
  359.  
  360. There were some other problems after those two, but they weren't nearly as 
  361. interesting.  One thing I learned that I did find very interesting though, is 
  362. that FS_EXIT in the IFS is not called if the ring 3 control program traps. 
  363. That turns out to be unfortunate because OS/2 won't put up the trap info, or 
  364. let the program die, until the memory that the IFS VMLock'ed has been 
  365. VMUnlock'ed, which is the whole purpose of the code for FS_EXIT in the IFS. 
  366. Because of that limitation, I'm considering modifying the design so that the 
  367. IFS allocates the shared memory instead of the control program, but that will 
  368. have to wait until next time.  In the mean time, I added another operation to 
  369. the IFS's FS_FSCTL entry point, FSCTL_FUNC_KILL, which will unlock the shared 
  370. memory and clear the semaphores.  FSKILL.C is a utility program that will make 
  371. that call.  This is extremely handy when debugging because you're bound to get 
  372. things hosed up, and it's nice to have a way to unhose them besides rebooting. 
  373.  
  374. I also added a another new program, FSDET.C, which will detach a drive from the 
  375. IFS.  After running FSATT a few times and associating a bunch of drive letters 
  376. with the IFS, it's nice to have a way to disassociate those drive letters, too. 
  377.  
  378. Select this to go to the next section 
  379.  
  380.  
  381. ΓòÉΓòÉΓòÉ 2.1.6. FS_ATTACH processing ΓòÉΓòÉΓòÉ
  382.  
  383. Finally, this month, I'd like to talk about how FS_ATTACH and FS_CHDIR should 
  384. be implemented.  Unfortunately, the IFS reference isn't real clear on how they 
  385. should work. 
  386.  
  387. FS_ATTACH is the function that is called whenever OS/2 needs to associate or 
  388. disassociate a drive letter with your IFS.  It's also called when OS/2 is 
  389. querying information about a particular attach. FS_ATTACH supports the 
  390. DosFSAttach() and DosQueryFSAttach() calls. 
  391.  
  392. When OS/2 wants to attach a drive, FS_ATTACH is called with flag set to 
  393. FSA_ATTACH.  pDev will contain the drive letter in the form 'R:', and pParm and 
  394. pLen will correspond to the pDataBuffer and ulDataBufferLen parameters on 
  395. DosFSAttach().  For our IFS, this area will contain a directory that the drive 
  396. letter should be associated with. 
  397.  
  398. The first thing the IFS has to do is verify that pDev contains a drive letter, 
  399. and not a device name.  IFS's can also be used to write character mode device 
  400. drivers, and for that type of IFS, FS_ATTACH is used to associate the IFS with 
  401. a device driver name.  All you have to do to check is see if the first 
  402. character of pDev is a '\' - if it is, somebody is trying to attach a device 
  403. driver name to the IFS, so you should just return with ERROR_NOT_SUPPORTED. 
  404.  
  405. The next thing you would if you were writing a pure ring 0 IFS would be to 
  406. verify that you can access the pParm data area, since OS/2 does not do that 
  407. automatically for you.  In our split ring 0/ring 3 IFS, the ring 0 side handles 
  408. that automatically, so the ring 3 side doesn't have to worry about it. 
  409.  
  410. Next, the IFS can proceed with the attach processing.  For our IFS, we do a 
  411. DosQueryPathInfo() to check that the directory the user passed in the pParm 
  412. area exists and is really a directory.  If it is, we allocate enough memory to 
  413. hold the directory and copy in there.  We then use part of the CDFSD to hold 
  414. the pointer to that memory. 
  415.  
  416. So what's a CDFSD?  IFS's have four types of structures it deals with, one each 
  417. for volume parameters, current directories, open files, and file searches. 
  418. Each one of those structures has a file system independent part, and a file 
  419. system dependent part.  The file system independent part is the same across 
  420. every IFS, while each IFS can use the file system dependent part however it 
  421. wants.  So to answer the question, a CDFSD is a file system dependent current 
  422. directory, and we use it to store a pointer to the text of the directory the 
  423. user asked us to attach to.  Descriptions of these structures can be found in 
  424. the IFS reference, although some of the fields are a bit difficult to 
  425. understand.  As we need them, though, I'll explain what each one does. 
  426.  
  427. At attach time, an IFS should also fill in the VPFSD, which is the file system 
  428. dependent volume parameters.  For our IFS, all we need is the same pointer 
  429. that's in the CDFSD.  This will be used on the FS_FSINFO and FS_FLUSHBUF calls 
  430. when we implement those. 
  431.  
  432. For a detach, flag will be set to FSA_DETACH.  The IFS should flush any 
  433. remaining buffers and deallocate any memory associated with the drive letter. 
  434. Currently, our IFS doesn't do a free on the memory we allocated at attach time 
  435. because it could get into trouble. Consider this scenario:  a user runs the 
  436. control program and attaches a drive (the control program allocates memory to 
  437. hold the directory).  The user then kills the control program without detaching 
  438. the drive, and then re-runs it.  At this point, OS/2 still considers the attach 
  439. the user did to be valid, even though the control program wouldn't.  Suppose 
  440. now the user does a detach.  If the control program tried to free the memory in 
  441. the CDFSD, it would trap because it never allocated that memory - the previous 
  442. instance of the control program did.  There is a way around that problem, but 
  443. for now we will just not free the memory. 
  444.  
  445. For a query, FS_ATTACH will be called with flag set to FSA_ATTACH_INFO.  pParm 
  446. will point to a structure that looks like this: 
  447.  
  448. struct attach_info {
  449.    unsigned short cbFSAData;
  450.    char rgFSAData[1];
  451. }
  452.  
  453. The IFS should fill in the rgFSAData with whatever information it wants to 
  454. return, and cbFSAData with the length of the information.  Our IFS just returns 
  455. the directory that the user originally specified when he attached.  The 
  456. information a particular IFS returns is completely up to the IFS, but generally 
  457. it is some sort of ASCII text.  To see what other IFS's return, you can use 
  458. FSATT with no parameters - it'll go through all the drive letters, do a 
  459. DosQueryFSAttach() on each, and print out what it returns. 
  460.  
  461. Select this to go to the next section 
  462.  
  463.  
  464. ΓòÉΓòÉΓòÉ 2.1.7. FS_CHDIR ΓòÉΓòÉΓòÉ
  465.  
  466. OS/2 handles current directories extremely intelligently.  It treats a current 
  467. directory not as just some piece of text, but as something that has to be 
  468. allocated and freed every time a user switches directories.  If two different 
  469. processes are in the same current directory, OS/2 can share a single current 
  470. directory structure between them and will free it only when both processes no 
  471. longer are using it.  It also only keeps one current directory structure for 
  472. the root directory (the one that we filled in at FS_ATTACH time).  This makes 
  473. FS_CHDIR a little more challenging to implement, but not too terribly 
  474. complicated. 
  475.  
  476. When a user issues a CD command, the OS/2 will call FS_CHDIR with flag set to 
  477. CD_EXPLICIT.  This indicates to the IFS that it should mutate the CDFSD that it 
  478. receives into the CDFSD appropriate for the directory being switched to.  What 
  479. OS/2 actually does is it copies the old CDFSD into new memory and calls the IFS 
  480. pointing to the new memory.  The IFS should do whatever verification it needs 
  481. to do, and then change the CDFSD appropriately, unless we are switching to the 
  482. root directory.  If we are switching to the root directory, OS/2 will throw 
  483. away whatever the IFS returns in CDFSD, and use the CDFSD that the IFS setup at 
  484. FS_ATTACH time. The IFS should not free any resources kept with the old CDFSD. 
  485.  
  486. The text of the new current directory is passed in the pDir pointer, and to 
  487. help optimize processing, iCurDirEnd points to the end of the old current 
  488. directory in pDir.  For example, if the old current directory was "F:\DIRECT1", 
  489. and we're switching to "F:\DIRECT1\DIRECT2", iCurDirEnd will point to second 
  490. '\'.  If iCurDirEnd is -1, it means that the user CD'd up the tree, such as 
  491. from "F:\LEVEL1\LEVEL2" to "F:\LEVEL1", or to "F:\LEVEL1\LEVEL2A", and this 
  492. optimization isn't possible. 
  493.  
  494. What our IFS does for this call is simply verify that the directory the user is 
  495. trying to switch to exists and really is a directory.  We don't actually keep 
  496. the text of the directory around, since we can recreate it whenever we need to 
  497. by concatenating the Dir field of the CSFSD and the cdi_curdir field of the 
  498. CDFSI (cdi_curdir is the text of the current directory). 
  499.  
  500. When OS/2 want to free a current directory structure, it calls FS_CHDIR with 
  501. flag set to CD_FREE.  The IFS should release any resources associated with the 
  502. current directory passed in. For us, we never allocated anything, so we don't 
  503. need to free anything. 
  504.  
  505. Finally, if OS/2 want to verify a current directory, it calls FS_CHDIR with 
  506. flag set to CD_VERIFY.  The IFS should verify that the current directory is a 
  507. valid directory name, and that it exists.  If it isn't a valid directory, it 
  508. should release any resources associated with the current directory (i.e. it 
  509. should pretend it got an FS_CHDIR/CD_FREE call).  Our IFS simply calls 
  510. DosQueryPathInfo() to verify that the directory still exists and that in fact 
  511. it is a directory. 
  512.  
  513. Select this to go to the next section 
  514.  
  515.  
  516. ΓòÉΓòÉΓòÉ 2.1.8. Next Time ΓòÉΓòÉΓòÉ
  517.  
  518. Well, that's it for now.  Next time I'll go over that state diagram, and also 
  519. start talking about the FS_FIND* routines.  At that point we'll have an IFS 
  520. where you'll be able to switch directories and do DIR's.  Good stuff! 
  521.  
  522. Select this to go to the next section 
  523.  
  524.  
  525. ΓòÉΓòÉΓòÉ 2.2. Rexx-ercising Your Applications - Part 2 ΓòÉΓòÉΓòÉ
  526.  
  527. Written by Gordon W. Zeglinski 
  528.  
  529. Introduction 
  530.  
  531. In part 1, we seen how to extend REXX by creating external functions and how to 
  532. execute REXX programs from within our applications. RexxStart() wasn't 
  533. thoroughly explored in part 1, so in this issue we will examine two more 
  534. permutations of RexxStart().  Also, in this issue, we will look at using 
  535. external command handlers and using the REXX macrospace. 
  536.  
  537. Enough preamble, let's get to it! 
  538.  
  539. Select this to go to the next section 
  540.  
  541.  
  542. ΓòÉΓòÉΓòÉ 2.2.1. Introduction ΓòÉΓòÉΓòÉ
  543.  
  544. Written by Gordon W. Zeglinski 
  545.  
  546. In part 1, we seen how to extend REXX by creating external functions and how to 
  547. execute REXX programs from within our applications. RexxStart() wasn't 
  548. thoroughly explored in part 1, so in this issue we will examine two more 
  549. permutations of RexxStart().  Also, in this issue, we will look at using 
  550. external command handlers and using the REXX macrospace. 
  551.  
  552. Enough preamble, let's get to it! 
  553.  
  554. Select this to go to the next section 
  555.  
  556.  
  557. ΓòÉΓòÉΓòÉ 2.2.2. More on RexxStart() ΓòÉΓòÉΓòÉ
  558.  
  559. Last time, we set the INSTORE parameter to NULL, to execute REXX procedures 
  560. from files.  By permuting the INSTORE parameter, we can execute REXX procedure 
  561. from memory or the macrospace. 
  562.  
  563. If you remember REXXSAMP.CPP from part 1, the variable Prog[] in the main() 
  564. function, declares an in-memory version of this program.  Prog[] is then 
  565. assigned to the RXSTRING INSTORE[0]. 
  566.  
  567. #define INCL_RXFUNC       /* external function values */
  568. #define INCL_VIO
  569. #include <rexxsaa.h>
  570. #include <iostream.h>
  571. #include <string.h>
  572.  
  573. ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
  574.                     PSZ *queuename, RXSTRING *retstr);
  575.  
  576.  
  577. ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
  578.                     PSZ *queuename, RXSTRING *retstr)
  579. {
  580.   BYTE bCell[2];
  581.  
  582. // If arguments, return non-zero to indicate error
  583.   if (numargs)
  584.     return 1;
  585.  
  586.   bCell[0] = 0x20;                     /* Space Character            */
  587.   bCell[1] = 0x07;                     /* Default Attrib             */
  588.   VioScrollDn( 0, 0, (USHORT)0xFFFF, (USHORT)0XFFFF,
  589.                      (USHORT)0xFFFF, bCell, (HVIO) 0);/* CLS         */
  590.   VioSetCurPos(0, 0, (HVIO) 0);        /*                Pos cursor  */
  591.  
  592. //return "0" to the caller
  593.   retstr->strlength=1;
  594.   strcpy(retstr->strptr,"0");
  595.  
  596.  
  597.   return 0;
  598. }
  599.  
  600. int main(){
  601.   char Input[200];
  602.   LONG      return_code;  /* interpreter return code    */
  603.   RXSTRING  argv[1];      /* program argument string    */
  604.   RXSTRING  retstr;       /* program return value       */
  605.   SHORT     rc;           /* converted return code      */
  606.   RXSTRING  INSTORE[2];
  607.   char      Prog[]=
  608.        "/* RexxSamp.cmd   */\r\n"
  609.        "/* Sample EDM/2 REXX program by Gordon Zeglinski */\r\n"
  610.        "\r\n"
  611.        "Call RxFuncAdd \'SysSleep\', \'REXXUTIL\', \'SysSleep\'\r\n"
  612.        "\r\n"
  613.        "say \"Sample EDM/2 REXX program started\"\r\n"
  614.        "\r\n"
  615.        "/*execute a cmd based command */\r\n"
  616.        "\'dir /w\'\r\n"
  617.        "\r\n"
  618.        "/* wait 5 seconds */\r\n"
  619.        "Call SysSleep 5\r\n"
  620.        "\r\n"
  621.        "/*clear screen using sample function */\r\n"
  622.        "call EDM_SysCls\r\n"
  623.        "\r\n"
  624.        "say\r\n"
  625.        "say\r\n"
  626.        "say \"Screen cleared by sample external REXX function\"\r\n\x1A";
  627.  
  628.  
  629.   RexxRegisterFunctionExe((PSZ)"EDM_SysCls",(PFN) &.SysCls);
  630.  
  631.  
  632.   INSTORE[0].strptr=Prog;
  633.   INSTORE[0].strlength=strlen(Prog);
  634.   INSTORE[1].strptr=NULL;
  635.   INSTORE[1].strlength=0;
  636.  
  637.  
  638.   cout<<"Sample EDM/2 Rexx Demonstration Program Part 1"<<endl<<"by Gordon Zeglinski"<<endl;
  639.   cout<<"Executing REXX programs via INSTORE parameter"<<endl;
  640.   cout<<endl<<"INSTORE[1].strptr\t"<<(void*)INSTORE[1].strptr;
  641.   cout<<endl<<"INSTORE[1].strlength\t"<<INSTORE[1].strlength<<endl;
  642.  
  643.  
  644.   if(!strlen(Input))
  645.      strcpy(Input,"RexxSamp.cmd");
  646.  
  647.   cout<<"Executing Sample Program "<<endl;
  648.   cout<<"-----------------"<<endl;
  649.  
  650.  
  651.   retstr.strptr=new char [1024];
  652.   retstr.strlength=1024;
  653.  
  654.   return_code = RexxStart(0,             // No arguments
  655.                         argv,            // dummy entry
  656.                         Input,           // File name
  657.                         INSTORE,         // InStore
  658.                         "CMD",           // use the "CMD" command processor
  659.                         RXCOMMAND,       // execute as a command
  660.                         NULL,            // No exit handlers
  661.                         &.rc,             // return code from REXX routine
  662.                         &.retstr);        // return string from REXX routine
  663.  
  664.   delete [] retstr.strptr;
  665.  
  666.   cout<<"After Execution";
  667.   cout<<endl<<"INSTORE[1].strptr\t"<<(void*)INSTORE[1].strptr;
  668.   cout<<endl<<"INSTORE[1].strlength\t"<<INSTORE[1].strlength<<endl;
  669.  
  670. return 0;
  671. }
  672.  
  673. Figure 1. Using INSTORE to execute REXX procedures in memory 
  674.  
  675. The above program should have the same output as REXXSAMP.CMD. We haven't done 
  676. anything too revolutionary yet, but it's a good start.  Next we'll see how to 
  677. set up INSTORE to execute macros. 
  678.  
  679. #define INCL_RXFUNC       /* external function values */
  680. #define INCL_RXMACRO
  681. #define INCL_VIO
  682. #include <rexxsaa.h>
  683. #include <iostream.h>
  684. #include <string.h>
  685.  
  686. ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
  687.                     PSZ *queuename, RXSTRING *retstr);
  688.  
  689.  
  690. ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
  691.                     PSZ *queuename, RXSTRING *retstr)
  692. {
  693.   BYTE bCell[2];
  694.  
  695. // If arguments, return non-zero to indicate error
  696.   if (numargs)
  697.     return 1;
  698.  
  699.   bCell[0] = 0x20;                     /* Space Character            */
  700.   bCell[1] = 0x07;                     /* Default Attrib             */
  701.   VioScrollDn( 0, 0, (USHORT)0xFFFF, (USHORT)0XFFFF,
  702.                      (USHORT)0xFFFF, bCell, (HVIO) 0);/* CLS         */
  703.   VioSetCurPos(0, 0, (HVIO) 0);        /*                Pos cursor  */
  704.  
  705. //return "0" to the caller
  706.   retstr->strlength=1;
  707.   strcpy(retstr->strptr,"0");
  708.  
  709.  
  710.   return 0;
  711. }
  712.  
  713.  
  714. int main(){
  715.   char Input[200];
  716.   LONG      return_code;  /* interpreter return code    */
  717.   RXSTRING  argv[2];      /* program argument string    */
  718.   RXSTRING  retstr;       /* program return value       */
  719.   SHORT      rc;           /* converted return code      */
  720.   RXSTRING  INSTORE[2];
  721.   char NumArg;
  722.  
  723.   RexxRegisterFunctionExe((PSZ)"EDM_SysCls",(PFN) &SysCls);
  724.  
  725.   INSTORE[0].strptr=NULL;
  726.   INSTORE[0].strlength=0;
  727.   INSTORE[1].strptr=NULL;
  728.   INSTORE[1].strlength=0;
  729.  
  730.  
  731.   cout<<"Sample EDM/2 Rexx Demonstration Program 2"<<endl<<"by Gordon Zeglinski"<<endl;
  732.   cout<<"Type rexxsamp.cmd <ENTER> to execute supplied smaple"<<endl;
  733.   cin>>Input;
  734.  
  735.   if(!strlen(Input))
  736.      strcpy(Input,"RexxSamp.cmd");
  737.  
  738.   cout<<"Executing Sample Program "<<Input<<endl;
  739.   cout<<"-----------------"<<endl;
  740.  
  741.   retstr.strptr=new char [1024];
  742.   retstr.strlength=1024;
  743.  
  744.   argv[0].strptr="";
  745.   argv[0].strlength=0;
  746.  
  747.   cout <<"Storing Macro"<<endl;
  748.  
  749.   return_code =RexxAddMacro("MACRO1", Input, RXMACRO_SEARCH_BEFORE);
  750.  
  751.   cout <<"Executing Macro"<<endl;
  752.  
  753.   return_code = RexxStart(0,             // No arguments
  754.                         argv,            // dummy entry
  755.                         "MACRO1",        // File name
  756.                         INSTORE,         // NULL InStore
  757.                         "CMD",           // use the "CMD" command processor
  758.                         RXCOMMAND,       // execute as a command
  759.                         NULL,            // No exit handlers
  760.                         &rc,             // return code from REXX routine
  761.                         &retstr);        // return string from REXX routine
  762.  
  763.   delete [] retstr.strptr;
  764.  
  765. return 0;
  766. }
  767.  
  768. Figure 2. Using INSTORE to Execute Macros 
  769.  
  770. Now we have explore two new ways to use RexxStart().  In part 1 and part 2 of 
  771. this article, we have now seen how to use RexxStart() to: 
  772.  
  773. o execute REXX programs from a file 
  774. o execute REXX programs from memory 
  775. o execute REXX programs from the macrospace 
  776.  
  777. So what is this macrospace thing and what does RexxAddMacro() function do? 
  778.  
  779. For the answer to these and other exciting questions, keep reading... 
  780.  
  781. Select this to go to the next section 
  782.  
  783.  
  784. ΓòÉΓòÉΓòÉ 2.2.3. The REXX Macrospace ΓòÉΓòÉΓòÉ
  785.  
  786. The macrospace is a storage area in memory where REXX can keep functions for 
  787. rapid execution.  The application can add, remove, query, and change the search 
  788. order of macros using the functions: 
  789.  
  790. o (ULONG)RexxAddMacro(PSZ FunctionName,PSZ SourceFile,ULONG SearchOrder) 
  791. o (ULONG)RexxDropMacro(PSZ FunctionName) 
  792. o (ULONG)RexxQueryMacro(PSZ FunctionName,PUSHORT Position) 
  793. o (ULONG)RexxReorderMacro(PSZ FunctionName,ULONG Position) 
  794.  
  795. For RexxAddMacro(), the following parameters are specified: 
  796.  
  797. FunctionName                  This is the name of the function that is to be 
  798.                               used in the RexxStart function call 
  799. SourceFile                    This is the name of the file in which the macro's 
  800.                               source code is stored. 
  801. SearchOrder                   either RXMACRO_SEARCH_BEFORE (search before 
  802.                               external functions or source files) or 
  803.                               RX_MACRO_SEARCH_AFTER (search after external 
  804.                               functions or source files) 
  805.  
  806. Return Value: 
  807.  
  808. RXMACRO_OK                    no error 
  809. RXMACRO_NO_STORAGE            macrospace is full 
  810. RXMACRO_SOURCE_NOT_FOUND      SourceFile is invalid 
  811. RXMACRO_INVALID_POSITION      SearchOrder is invalid 
  812.  
  813. For RexxDropMacro(), the following parameters are specified: 
  814.  
  815. FunctionName                  This is the name of the function that was used in 
  816.                               the RexxAddMacro() function call 
  817.  
  818. Return Value: 
  819.  
  820. RXMACRO_OK                    no error 
  821. RXMACRO_NOT_FOUND             macro name is not registered 
  822.  
  823. For RexxQueryMacro(), the following parameters are specified: 
  824.  
  825. FunctionName                  This is the name of the function whose existence 
  826.                               is being queried 
  827. Position                      If the macro is found, its search order is 
  828.                               returned here. 
  829.  
  830. Return Value: 
  831.  
  832. RXMACRO_OK                    no error 
  833. RXMACRO_NOT_FOUND             macro name is not registered 
  834.  
  835. For RexxReorderMacro(), the following parameters are specified: 
  836.  
  837. FunctionName                  This is the name of the function that is to be 
  838.                               used in the RexxStart() function call 
  839. SearchOrder                   either RXMACRO_SEARCH_BEFORE or 
  840.                               RX_MACRO_SEARCH_AFTER 
  841.  
  842. Return Value: 
  843.  
  844. RXMACRO_OK                    no error 
  845. RXMACRO_NOT_FOUND             macro name is not registered 
  846. RXMACRO_INVALID_POSITION      SearchOrder is invalid 
  847.  
  848. Manipulating the MACRO Storage Area 
  849.  
  850. In addition to manipulating the functions within the storage area, REXX also 
  851. allows us to manipulate the storage area itself.  The storage area can be saved 
  852. to disk, loaded from disk and erased using the functions: 
  853.  
  854. o (ULONG)RexxSaveMacroSpace(ULONG Num,PSZ* List,PSZ FileName) 
  855. o (ULONG)RexxLoadMacroSpace(ULONG Num,PSZ* List,PSZ FileName) 
  856. o (ULONG)RexxClearMacroSpace(VOID) 
  857.  
  858. For RexxSaveMacroSpace(), the following parameters are specified: 
  859.  
  860. Num                           The number of function names in List (if Num is 
  861.                               0, then save the whole macrospace) 
  862. List                          List of function names to be saved (if Num is 0, 
  863.                               then this can be NULL) 
  864. FileName                      The name of the file in which the macrospace will 
  865.                               be saved. 
  866.  
  867. Return Value: 
  868.  
  869. RXMACRO_OK                    no error 
  870. RXMACRO_NOT_FOUND             a name in List is not registered 
  871. RXMACRO_EXTENSION_REQUIRED    FileName must have an extension 
  872. RXMACRO_FILE_ERROR            An error occurred while accessing the file given 
  873.                               by FileName 
  874.  
  875. The parameters for RexxLoadMacroSpace() are the same as those for 
  876. RexxSaveMacroSpace() 
  877.  
  878. Return Value: 
  879.  
  880. RXMACRO_OK                    no error 
  881. RXMACRO_NO_STORAGE            macrospace ran out while loading in the saved 
  882.                               file 
  883. RXMACRO_NOT_FOUND             a name in List is not in the saved file 
  884. RXMACRO_ALREADY_EXISTS        a name in List is already in the macrospace 
  885. RXMACRO_FILE_ERROR            an error occurred while accessing the file given 
  886.                               by FileName 
  887. RXMACRO_SIGNATURE_ERROR       the file is not a valid saved macrospace file or 
  888.                               it is corrupted. 
  889.  
  890. The use of these three functions are illustrated in the file rexx23.cpp.  The 
  891. macrospace saved files are not portable across different versions of the REXX 
  892. interpreter.  In this case, the original source files would have to be read in 
  893. using RexxAddMacro(). 
  894.  
  895. Select this to go to the next section 
  896.  
  897.  
  898. ΓòÉΓòÉΓòÉ 2.2.4. External Subcommand Handlers ΓòÉΓòÉΓòÉ
  899.  
  900. Unlike the macrospace functions, which is more for the user than the 
  901. application developer, the external subcommand handler functions are used by 
  902. the developer to extend the REXX language.  The following functions are 
  903. provided to the developer: 
  904.  
  905. o (ULONG)RexxRegisterSubcomDll(PSZ Name,PSZ ModuleName,PSZ FunctionName,PUCHAR 
  906.   Data,ULONG Drop) 
  907. o (ULONG)RexxRegisterSubcomExe(PSZ Name,PFN FunctionAdr,PUCHAR Data) 
  908. o (ULONG)RexxDeregisterSubcom(PSZ Name,PSZ ModuleName) 
  909. o (ULONG)RexxQuerySubcom(PSZ Name,PSZ ModuleName,PUSHORT Flag,PUCHAR Data) 
  910.  
  911. For RexxRegisterSubcomDll(), the following parameters are specified: 
  912.  
  913. Name                          environment name 
  914. ModuleName                    name of the DLL 
  915. FunctionName                  name of the function in the DLL 
  916. Data                          8 bytes of user data. (Can be NULL) 
  917. Drop                          either RXSUBCOM_DROPPABLE (any process can 
  918.                               deregister the handler) or RXSUBCOM_NONDROP (only 
  919.                               the current process can deregister the handler) 
  920.  
  921. Return Value: 
  922.  
  923. RXSUBCOM_OK                   no error 
  924. RXSUBCOM_DUP                  the environment name has already been defined (to 
  925.                               address this handler, its library name must be 
  926.                               specified) 
  927. RXSUBCOM_NOEMEM               Not enough memory 
  928. RXSUBCOM_BADTYPE              Drop is invalid 
  929.  
  930. For RexxRegisterSubcomExe(), the following parameters are specified: 
  931.  
  932. Name                          environment name 
  933. FunctionAdr                   the address of the function 
  934. Data                          8 bytes of user data. (Can be NULL) 
  935.  
  936. Return Value: 
  937.  
  938. RXSUBCOM_OK                   no error 
  939. RXSUBCOM_DUP                  the environment name has already been defined 
  940. RXSUBCOM_NOTREG               not registered because of duplicate name 
  941. RXSUBCOM_NOEMEM               not enough memory 
  942.  
  943. For RexxDeregisterSubcom(), the following parameters are specified: 
  944.  
  945. Name                          environment name 
  946. ModuleName                    name of the DLL in which the handler function 
  947.                               resides 
  948.  
  949. Return Value: 
  950.  
  951. RXSUBCOM_OK                   no error 
  952. RXSUBCOM_NOTREG               the environment name has not been registered 
  953. RXSUBCOM_NOCANDROP            drop permission is not granted 
  954. RXSUBCOM_BADTYPE              Subcom is invalid 
  955.  
  956. For RexxQuerySubcom(), the following parameters are specified: 
  957.  
  958. Name                          environment name 
  959. ModuleName                    name of the DLL in which the handler function 
  960.                               resides 
  961. Flag                          either RXSUBCOM_OK or RXSUBCOM_NOTREG depending 
  962.                               upon whether Name is registered or not. 
  963. Data                          The address to an 8 byte location in memory to 
  964.                               receive the user data 
  965.  
  966. Return Value: 
  967.  
  968. RXSUBCOM_OK                   no error 
  969. RXSUBCOM_NOTREG               the environment name has not been registered 
  970. RXSUBCOM_BADTYPE 
  971.  
  972. Creating External Subcommand Handler Functions 
  973.  
  974. External command handlers must use the following format: 
  975.  
  976. ULONG _System command_handler(PRXSTRING Command,PUSHORT Flag, PRXSTRING Retstr)
  977.  
  978. The arguments have the following meaning: 
  979.  
  980. Command                       a null-terminated RXSTRING containing the issued 
  981.                               command 
  982. Flag                          the subroutine uses this flag to return the 
  983.                               completion status of the command.  It must be 
  984.                               either RXSUBCOM_OK, RXSUBCOM_ERROR, or 
  985.                               RXSUBCOM_FAILURE 
  986. Retstr                        Address of an RXSTRING for the return code. 
  987.                               Retstr is a character string return code that 
  988.                               will be assigned to the REXX special variable RC 
  989.                               when the subcommand handler returns.  By default, 
  990.                               Retstr points to a 256-byte RXSTRING.  If 
  991.                               necessary, a longer RXSTRING can allocated with 
  992.                               DosAllocMem().  If the subcommand handler sets 
  993.                               Retval to an empty RXSTRING, REXX will assign the 
  994.                               string "0" to RC. 
  995.  
  996. Flag Interpretation 
  997.  
  998. RXSUBCOM_OK                   Indicates that the command was successfully 
  999.                               executed. 
  1000. RXSUBCOM_ERROR                Indicates that the command is recognized by the 
  1001.                               handler but a syntax error occurred (incorrect 
  1002.                               parameters for instance).  The REXX interpreter 
  1003.                               will raise this condition if SIGNAL ON ERROR  or 
  1004.                               CALL ON ERROR traps have been created.  If TRACE 
  1005.                               ERRORS has been issued, REXX will trace the 
  1006.                               command when the subcommand handler returns. 
  1007. RXSUBCOM_FAILURE              Incidactes that a subcommand failure has 
  1008.                               occurred.  Usually this is used if Command is 
  1009.                               unrecognized.  The REXX interpreter will raise 
  1010.                               this condition if SIGNAL ON FAILURE or CALL ON 
  1011.                               FAILURE traps have been created. If TRACE 
  1012.                               FAILURES has been issued, REXX will trace the 
  1013.                               command when the subcommand handler returns. 
  1014.  
  1015. If the command has parameters, it is up to the subcommand handler to parse the 
  1016. command for the arguments.  The following simple subcommand handler processes 
  1017. the command "CLS". 
  1018.  
  1019. ULONG _System EDM_Handler(PRXSTRING Command,PUSHORT Flags,PRXSTRING retstr){
  1020.    BYTE bCell[2];
  1021.  
  1022.    if(!strcmp("CLS",Command->strptr ) ){
  1023.       bCell[0] = 0x20;
  1024.       bCell[1] = 0x07;
  1025.       VioScrollDn( 0, 0, (USHORT)0xFFFF, (USHORT)0XFFFF,
  1026.                      (USHORT)0xFFFF, bCell, (HVIO) 0);
  1027.       VioSetCurPos(0, 0, (HVIO) 0);
  1028.  
  1029.       *Flags=RXSUBCOM_OK;
  1030.       retstr->strlength=1;
  1031.       strcpy(retstr->strptr,"0");
  1032.       return 0;
  1033.    }
  1034.    *Flags=RXSUBCOM_FAILURE;
  1035.     retstr->strlength=1;
  1036.     strcpy(retstr->strptr,"1");
  1037.     return 1;
  1038. }
  1039.  
  1040. Select this to go to the next section 
  1041.  
  1042.  
  1043. ΓòÉΓòÉΓòÉ 2.2.5. Putting It All Together ΓòÉΓòÉΓòÉ
  1044.  
  1045. The file rexx24.cpp contains a sample program demonstrating the use of external 
  1046. subcommand handlers, external functions, and executing in memory REXX programs. 
  1047. It is rather long, and very similar to the other sample REXX programs that are 
  1048. included in this article, so it won't be included here.  However, let's look at 
  1049. two code snippets that illustrate new points. 
  1050.  
  1051. The external subcommand handler is registered by the function call: 
  1052.  
  1053. RexxRegisterSubcomExe((PSZ)"EDM",(PFN)&EDM_Handler,NULL);
  1054.  
  1055. The external subcommand handler we have seen in the previous section is 
  1056. registered under the name "EDM".  To use this handler, our RexxStart() must be 
  1057. modified to specify the "EDM" environment.  The following section of code 
  1058. illustrates this: 
  1059.  
  1060.   return_code = RexxStart(0,             // No arguments
  1061.                         argv,            // dummy entry
  1062.                         "Sample",        // procedure name
  1063.                         INSTORE,         // InStore
  1064.                         "EDM",           // use the "EDM" command processor
  1065.                         RXCOMMAND,       // execute as a command
  1066.                         NULL,            // No exit handlers
  1067.                         &rc,             // return code from REXX routine
  1068.                         &retstr);        // return string from REXX routine
  1069.  
  1070. Functions, Macros, and Commands 
  1071.  
  1072. We have now examined a good portion of the features REXX provides developers. 
  1073. A few questions now arise like:  what's the difference between commands and 
  1074. functions, and where do macros fit into this? 
  1075.  
  1076. Let's start by looking at the difference between functions and commands. 
  1077. Functions require that the user (the person using your application/library) to 
  1078. use a bulky syntax.  For example, to call a function that clears the screen, 
  1079. let's call it "CLS", the user would have to use the either of the following 
  1080. syntaxes. 
  1081.  
  1082. call CLS
  1083.  
  1084. or 
  1085.  
  1086. dummy=CLS()
  1087.  
  1088. In the first case, CLS() returns a result in the special REXX variable RESULT. 
  1089. In the second case, the variable dummy is assigned the return value from CLS(). 
  1090. Both expressions are rather bulky considering that CLS() takes no arguments, 
  1091. and really doesn't have any return values other than TRUE or FALSE  If 
  1092. developing a REXX programming environment, CLS would be a prime candidate for 
  1093. command status, requiring only the following statement to execute: 
  1094.  
  1095. CLS
  1096.  
  1097. The return value for the command would be placed in the special REXX variable 
  1098. RC.  As previously mentioned, subcommand handler must parse their own commands. 
  1099. So if arguments are needed, it is less work for the programmer to use 
  1100. functions. 
  1101.  
  1102. As mentioned before, macros really aren't there for the programmer, they are 
  1103. for the user.  REXX provides several functions to allow the programmer to 
  1104. manipulate the macrospace.  Macros are written in REXX, while external 
  1105. functions and subcommand handlers are written in C/C++, or some other compiled 
  1106. language. 
  1107.  
  1108. Select this to go to the next section 
  1109.  
  1110.  
  1111. ΓòÉΓòÉΓòÉ 2.2.6. Summary ΓòÉΓòÉΓòÉ
  1112.  
  1113. This concludes our look at Rexx/2 for this issue.  Building upon the REXX 
  1114. information in part 1, we have covered new uses for RexxStart(), external 
  1115. subcommand handlers, and the REXX macrospace interface.  We have not looked at 
  1116. exit handlers or the various REXX interfaces for interactive debugging. 
  1117.  
  1118. As usual, question and comments are welcome. 
  1119.  
  1120. Select this to go to the next section 
  1121.  
  1122.  
  1123. ΓòÉΓòÉΓòÉ 2.3. Using OS/2 2.x bitmap files ΓòÉΓòÉΓòÉ
  1124.  
  1125. Written by Timur Tabi 
  1126.  
  1127. Introduction 
  1128.  
  1129. This article discusses the OS/2 2.x bitmap file format and how to use it in 
  1130. your applications.  OS/2 provides support for bitmaps contained in your 
  1131. executable's resources and they can be loaded with a single API call. However, 
  1132. there may be times when combining your bitmaps with the executable is not an 
  1133. option; unfortunately, OS/2 has no built-in support for bitmaps stored as .BMP 
  1134. files.  This article provides the missing elements. 
  1135.  
  1136. It should be noted that the information article will eventually be obsolete. 
  1137. Rumor has it that IBM is working on a providing built-in support for loading 
  1138. bitmaps, including those in other graphic formats like JPEG. There are also 
  1139. plans for a real image editor, something that is badly needed. 
  1140.  
  1141. The bitmap file format is also used for icons and pointers, but these two are 
  1142. beyond the scope of this article.  The PM Reference distinguishes between 
  1143. bitmaps and bit maps.  Bitmaps are conventional bitmap images, the kind found 
  1144. in .BMP files, and they are the subject of this article.  A bit map is either a 
  1145. bitmap, an icon, or a pointer. 
  1146.  
  1147. Nota Bene:  A lot of the information on the bitmap file format is either 
  1148. undocumented or so rarely used that no one outside IBM (including me) actually 
  1149. knows what it is.  If anyone has any information on the bitmap file format that 
  1150. is not in this article (for instance, the halftoning algorithms), please let me 
  1151. know.  I'll include it in my second edition! 
  1152.  
  1153. Select this to go to the next section 
  1154.  
  1155.  
  1156. ΓòÉΓòÉΓòÉ 2.3.1. Introduction ΓòÉΓòÉΓòÉ
  1157.  
  1158. Written by Timur Tabi 
  1159.  
  1160. This article discusses the OS/2 2.x bitmap file format and how to use it in 
  1161. your applications.  OS/2 provides support for bitmaps contained in your 
  1162. executable's resources and they can be loaded with a single API call. However, 
  1163. there may be times when combining your bitmaps with the executable is not an 
  1164. option; unfortunately, OS/2 has no built-in support for bitmaps stored as .BMP 
  1165. files.  This article provides the missing elements. 
  1166.  
  1167. It should be noted that the information article will eventually be obsolete. 
  1168. Rumor has it that IBM is working on a providing built-in support for loading 
  1169. bitmaps, including those in other graphic formats like JPEG. There are also 
  1170. plans for a real image editor, something that is badly needed. 
  1171.  
  1172. The bitmap file format is also used for icons and pointers, but these two are 
  1173. beyond the scope of this article.  The PM Reference distinguishes between 
  1174. bitmaps and bit maps.  Bitmaps are conventional bitmap images, the kind found 
  1175. in .BMP files, and they are the subject of this article.  A bit map is either a 
  1176. bitmap, an icon, or a pointer. 
  1177.  
  1178. Nota Bene:  A lot of the information on the bitmap file format is either 
  1179. undocumented or so rarely used that no one outside IBM (including me) actually 
  1180. knows what it is.  If anyone has any information on the bitmap file format that 
  1181. is not in this article (for instance, the halftoning algorithms), please let me 
  1182. know.  I'll include it in my second edition! 
  1183.  
  1184. Select this to go to the next section 
  1185.  
  1186.  
  1187. ΓòÉΓòÉΓòÉ 2.3.2. The OS/2 2.x Bitmap File Format ΓòÉΓòÉΓòÉ
  1188.  
  1189. OS/2 1.x used a bitmap file format very similar to the Windows 3.x DIB format. 
  1190. OS/2 2.x provides a newer format that offers more features like compression, 
  1191. different units of measurement, half-toning, different color encoding schemes, 
  1192. and user-defined space.  Unfortunately, many of these advanced options are not 
  1193. documented and therefore rarely used. 
  1194.  
  1195. OS/2 2.x supports both the old and the new bitmap formats.  However, use of the 
  1196. old format is strongly discouraged, and this article is only concerned with the 
  1197. new format. 
  1198.  
  1199. Bitmaps are divided into three parts: the bitmap information structure, the 
  1200. color table, and the actualy pel data. 
  1201.  
  1202. Select this to go to the next section 
  1203.  
  1204.  
  1205. ΓòÉΓòÉΓòÉ 2.3.3. Bitmap information structure ΓòÉΓòÉΓòÉ
  1206.  
  1207. Structure BITMAPINFOHEADER2 contains information that defines the size and type 
  1208. of the bitmap, but it says nothing about the colors used or the actual image. 
  1209.  
  1210. typedef struct _BITMAPINFOHEADER2 {
  1211.   ULONG cbFix;
  1212.   ULONG cx;
  1213.   ULONG cy;
  1214.   USHORT cPlanes;
  1215.   USHORT cBitCount;
  1216.   ULONG ulCompression;
  1217.   ULONG cbImage;
  1218.   ULONG cxResolution;
  1219.   ULONG cyResolution;
  1220.   ULONG cclrUsed;
  1221.   ULONG cclrImportant;
  1222.   USHORT usUnits;
  1223.   USHORT usReserved;
  1224.   USHORT usRecording;
  1225.   USHORT usRendering;
  1226.   ULONG cSize1;
  1227.   ULONG cSize2;
  1228.   ULONG ulColorEncoding;
  1229.   ULONG ulIdentifier;
  1230. } BITMAPINFOHEADER2;
  1231.  
  1232. cbFix               The size of the structure.  Set it to 
  1233.                     sizeof(BITMAPINFOHEADER2). 
  1234.  
  1235.                     [Editor's note - I do not normally do this, but since I 
  1236.                     know a fair amount about this topic, I will add that this 
  1237.                     is not necessarily true. The BITMAPINFOHEADER2 structure is 
  1238.                     defined semantically to be a variable-length structure 
  1239.                     meaning that you initialize up to the last field needed and 
  1240.                     set this field to be the size of the initialized portion of 
  1241.                     the structure.  Thus, if you do not need any of the 
  1242.                     advanced features, you only initialize cx, cy, cPlanes, and 
  1243.                     cBitCount and set this field to 16, which is the sum of 
  1244.                     these fields' sizes.] 
  1245. cx                  The true width of the bitmap, in pixels 
  1246. cy                  The true height of the bitmap, in pixels 
  1247. cPlanes             The number of colors planes.  I've never seen this set to 
  1248.                     anything other than 1. 
  1249. cBitCount           The number of bits/pixel, either 1, 4, 8, or 24. 
  1250. ulCompression       The compression scheme used.  These are documented in the 
  1251.                     PM Reference and can be any one of the following: 
  1252.  
  1253.    o BCA_UNCOMP - Uncompressed 
  1254.    o BCA_HUFFMAN1D - Huffman encoding, monochrome bitmaps only 
  1255.    o BCA_RLE4 - Run-length encoded, 4 bits/pixel 
  1256.    o BCA_RLE8 - RLE, 8/bit pixel 
  1257.    o BCA_RLE24 - RLE, 24 bits/pixel 
  1258. cbImage             The number of bytes that the pel data occupies.  For an 
  1259.                     uncompressed image, this should be initialized to zero. 
  1260. cxResolution        The horizontal resolution of the target device. 
  1261. cyResolution        The vertical resolution of the target device. 
  1262. cclrUsed            The number of colors in the color table.  This field is 
  1263.                     used if the color table is smaller than the maximum number 
  1264.                     of colors.  That is, if this is a 256-color bitmap, and the 
  1265.                     color table only defines 120 distinct colors in the bitmap, 
  1266.                     then this field is set to 120.  If it's set to zero, then 
  1267.                     this is either a 24-bit bitmap of RGB values, or the color 
  1268.                     table is full-length. 
  1269. cclrImportant       The number of colors in the color table that are required 
  1270.                     for satisfactory display of the bitmap.  This field is used 
  1271.                     in case the system cannot map all of the colors in the 
  1272.                     table to the physical palette, so it tries to map only the 
  1273.                     first cclrImportant colors, and it approximates the rest. 
  1274. usUnits             The units in which cxResolution and cyResolution are 
  1275.                     specified. Currently, only BRU_METRIC (pixels per meter) is 
  1276.                     supported. 
  1277. usReserved          This is a reserved field and must be set to zero. 
  1278. usRecording         The recording order of the pel data.  The only supported 
  1279.                     value is BRA_BOTTOMUP, which means that the first row of 
  1280.                     pel data is the bottom row of the image. 
  1281. usRendering         The halftoning algorithm for this image.  None of these is 
  1282.                     documented anywhere. 
  1283.  
  1284.    o BRH_NOTHALFTONED - This is the default. 
  1285.    o BRH_ERRORDIFFUSION - (Damped) error diffusion. 
  1286.    o BRH_PANDA - Processing Algorithm for Non-coded Document Acquisition. 
  1287.    o BRH_SUPERCIRCLE - Super Circle. 
  1288. cSize1              A parameter for the halftoning algorithm.  It's not used if 
  1289.                     there's no halftoning. 
  1290. cSize2              The second paramater for the PANDA and Super Circle 
  1291.                     algorithms. 
  1292. ulColorEncoding     The type of values in the color table.  The only supported 
  1293.                     value is BCE_RGB, which means the color table is an array 
  1294.                     of RGB2 structures. 
  1295. ulIdentifier        Application-specific data.  Use this ULONG for your own 
  1296.                     information.  Great for storing pointers to additional data 
  1297.                     structures or functions, or even to C++ objects. 
  1298.  
  1299. Select this to go to the next section 
  1300.  
  1301.  
  1302. ΓòÉΓòÉΓòÉ 2.3.4. The Color Table ΓòÉΓòÉΓòÉ
  1303.  
  1304. The color table is an array of RGB2 structures, which are defined in pmbitmap.h 
  1305. as: 
  1306.  
  1307. typedef struct _RGB2 {
  1308.    BYTE bBlue;
  1309.    BYTE bGreen;
  1310.    BYTE bRed;
  1311.    BYTE fcOptions;
  1312. } RGB2;
  1313.  
  1314. The first three fields are the values of blue, red, and green, respectively. 
  1315. Each is an eight-bit value, and together they combine to form a 24-bit color 
  1316. value, which is the maximum that OS/2 can handle.  There is also a 32-bit color 
  1317. standard, where another eight-bit value is used to indicate the transparency, 
  1318. but there is no support for this standard in OS/2. 
  1319.  
  1320. The fourth field in RGB2 is the options field.  There are two flags available, 
  1321. PC_RESERVED and PC_EXPLICIT. PC_RESERVED is used for animating colors with the 
  1322. GpiAnimatePalette() API.  The PM Reference online documentation provides this 
  1323. cryptic exaplanation for PC_EXPLICIT: 
  1324.  
  1325. "The low-order word of the color table entry designates a physical palette 
  1326. slot.  This allows an application to show the actual contents of the device 
  1327. palette as realized for other logical palettes.  This does not prevent the 
  1328. color in the slot from being changed for any reason." 
  1329.  
  1330. I have yet to find any mention of this field in any other documentation. 
  1331. Fortunately, its meaning is not important for our uses - the fcOptions field is 
  1332. always set to zero. 
  1333.  
  1334. The color table is nothing more than an array of RGB2 structures. The length of 
  1335. the array defines the number of colors in the bitmap.  Ideally, the values of 
  1336. the color table will exactly match the current system palette.  If not, OS/2 
  1337. will automatically handle conversions.  In either case, you don't have to worry 
  1338. about it. 
  1339.  
  1340. Select this to go to the next section 
  1341.  
  1342.  
  1343. ΓòÉΓòÉΓòÉ 2.3.5. The Bitmap Pel Data ΓòÉΓòÉΓòÉ
  1344.  
  1345. The bitmap pel data is the actual bitmap data, stored as a two-dimensional 
  1346. array in row-major order.  The dimensions of the bitmap are specified in the 
  1347. BITMAPINFOHEADER2 structure.  Although a bitmap can be of any size, each row of 
  1348. data is padded to the nearest doubleword boundry.  In other words, a 256-color 
  1349. bitmap (eight bits/pixel) that is five pixels wide would normally take five 
  1350. bytes for each row.  In actuality, it takes up eight bytes per row, where the 
  1351. last three bytes are all zeros. 
  1352.  
  1353. Each pixel is represented by an N-bit value in the pel data.  A 256-color 
  1354. bitmap uses one byte for each pixel, and a 24-bit bitmap uses three bytes per 
  1355. pixel.  The color table usually contains entries only for those colors used in 
  1356. the bitmap, although it may contain the entire palette.  A 24-bit bitmap does 
  1357. not require a color table, although the Icon Editor tends to include one 
  1358. anyway. 
  1359.  
  1360. Select this to go to the next section 
  1361.  
  1362.  
  1363. ΓòÉΓòÉΓòÉ 2.3.6. Single and Multiple bitmap files ΓòÉΓòÉΓòÉ
  1364.  
  1365. The OS/2 bitmap file format also supports multiple bitmaps per file.  A single 
  1366. bitmap file is stored in this manner: 
  1367.  
  1368. BITMAPFILEHEADER2
  1369.    BITMAPINFOHEADER2
  1370. Color table
  1371. Pel Data
  1372.  
  1373. BITMAPFILEHEADER2 is defined as: 
  1374.  
  1375. typedef struct _BITMAPFILEHEADER2 {
  1376.    USHORT usType;
  1377.    ULONG cbSize;
  1378.    SHORT xHotspot;
  1379.    SHORT yHotspot;
  1380.    ULONG offBits;
  1381.    BITMAPINFOHEADER2 bmp2;
  1382. } BITMAPFILEHEADER2;
  1383.  
  1384. usType              The type of image, BFT_BMAP for our purposes 
  1385. cbSize              Set to sizeof(BITMAPFILEHEADER2) 
  1386.  
  1387.                     [Don't forget that the bmp2 field is a variable length 
  1388.                     structure.  Adjust this value accordingly.  - Editor] 
  1389. xHotspot, yHotspot  The hotspot for icons and pointers, not used for bitmaps 
  1390. offBits             Points to the pel data for this bitmap 
  1391. bmp2                The BITMAPINFOHEADER2 structure as described in the section 
  1392.                     Bitmap information structure 
  1393.  
  1394. From this one structure, two out of the three parts of the bitmap are readily 
  1395. available - the info structure and the pel data.  The third part, the color 
  1396. table, is located immediately after the info structure.  The pointer 
  1397. manipulations are a bit tricky, but it only takes a few lines of code to get 
  1398. all three parts. 
  1399.  
  1400. When multiple bitmaps are present, each is enclosed in a BITMAPARRAYFILEHEADER2 
  1401. structure.  For example, this is the layout of a bitmap file with two bitmaps: 
  1402.  
  1403. BITMAPARRAYFILEHEADER2 (for bitmap #1)
  1404.    BITMAPFILEHEADER2
  1405.       BITMAPINFOHEADER2
  1406.    Color table
  1407. BITMAPARRAYFILEHEADER2 (for bitmap #2)
  1408.    BITMAPFILEHEADER2
  1409.       BITMAPINFOHEADER2
  1410.    Color table
  1411.    Pel Data (for bitmap #1)
  1412.    Pel Data (for bitmap #2)
  1413.  
  1414. The BITMAPARRAYFILEHEADER2 structure is described below. 
  1415.  
  1416. typedef struct _BITMAPARRAYFILEHEADER2 {
  1417.    USHORT usType;
  1418.    ULONG cbSize;
  1419.    ULONG offNext;
  1420.    USHORT cxDisplay;
  1421.    USHORT cyDisplay;
  1422.    BITMAPFILEHEADER2 bfh2;
  1423. } BITMAPARRAYFILEHEADER2;
  1424.  
  1425. usType              Set to BFT_BITMAPARRAY, which indicates this as an array of 
  1426.                     bit maps. 
  1427. cbSize              Set to sizeof(BITMAPARRAYFILEHEADER2). 
  1428. offNext             Points to the next BITMAPARRAYFILEHEADER2, or 0 if this is 
  1429.                     the last one. 
  1430. cxDisplay, cyDisplay The resolution of the target display for this bitmap.  See 
  1431.                     the section Using Multiple Bitmaps for an example of how to 
  1432.                     use these fields. 
  1433. bfh2                The BITMAPFILEHEADER2 structure as described earlier in 
  1434.                     this section. 
  1435.  
  1436. Select this to go to the next section 
  1437.  
  1438.  
  1439. ΓòÉΓòÉΓòÉ 2.3.7. Creating a Bitmap from a .BMP file ΓòÉΓòÉΓòÉ
  1440.  
  1441. Creating an OS/2 bitmap from a .BMP file requires three steps: 
  1442.  
  1443.  1. Load the bitmap file into memory 
  1444.  2. Locate the three main pieces of the bitmap file 
  1445.  3. Use GpiCreateBitmap() to create a bitmap 
  1446.  
  1447. The first part is easy.  The following code opens the file, finds its length, 
  1448. allocates enough memory for it, and loads it into that memory. 
  1449.  
  1450. #include <iostream.h>
  1451. #include <io.h>
  1452. #include <fcntl.h>
  1453. #include <malloc.h>
  1454.  
  1455. int load(const char *szName, char* &bitmap) {
  1456.   int fh=_open(szName,O_RDONLY | O_BINARY);
  1457.   if (fh == -1) {
  1458.     cout << "Error opening file " << szName << ".\n";
  1459.     return 0;
  1460.   }
  1461.  
  1462.   int length=_filelength(fh);
  1463.   if (length == -1) {
  1464.     cout << "Error determining length for " << szName << ".\n";
  1465.     _close(fh);
  1466.     return 0;
  1467.   }
  1468.   if (!length) {
  1469.     cout << szName << " has zero filesize.\n";
  1470.     _close(fh);
  1471.     return 0;
  1472.   }
  1473.  
  1474.   bitmap=(char *) malloc(length);
  1475.   if (!bitmap) {
  1476.     cout << "Error allocating " << length << " bytes.\n";
  1477.     _close(fh);
  1478.     return 0;
  1479.   }
  1480.  
  1481.   int bytesread=_read(fh,bitmap,length);
  1482.  
  1483.   if (!bytesread) {
  1484.     cout << "Read past end of file " << szName << ".\n";
  1485.     free(bitmap);
  1486.     _close(fh);
  1487.     return 0;
  1488.   }
  1489.   if (bytesread == -1) {
  1490.     cout << "Error reading file " << szName << ".\n";
  1491.     free(bitmap);
  1492.     _close(fh);
  1493.     return 0;
  1494.   }
  1495.   if (bytesread != length) {
  1496.     cout << "Could only read " << bytesread << " of " << length << " bytes.\n";
  1497.     free(bitmap);
  1498.     _close(fh);
  1499.     return 0;
  1500.   }
  1501.  
  1502.   if (_close(fh)) {
  1503.     cout << "Error closing " << szName << ".\n";
  1504.     free(bitmap);
  1505.     return 0;
  1506.   }
  1507.  
  1508.   return length;
  1509. }
  1510.  
  1511. The first parameter is the name of the bitmap file to load.  The second is a 
  1512. reference to a pointer to a block of memory.  This function allocates the 
  1513. correct amount of memory itself and returns the pointer to that block.  If all 
  1514. goes well, then the length of the block is returned.  Otherwise, a zero 
  1515. indicates failure. 
  1516.  
  1517. Select this to go to the next section 
  1518.  
  1519.  
  1520. ΓòÉΓòÉΓòÉ 2.3.8. Parsing a Bitmap File ΓòÉΓòÉΓòÉ
  1521.  
  1522. Once the bitmap is loaded into memory, its three subdivision must be located. 
  1523. The following code parses a single bitmap file: 
  1524.  
  1525. void parse(PBITMAPFILEHEADER2 pbfh, PBYTE pbBmpFile) {
  1526.   PBITMAPINFOHEADER2 pbih=&pbfh->bmp2;
  1527.   PRGB2 prgb=((PBITMAPINFO2) pbih)->argbColor;
  1528.   PBYTE pbPelData=pbBmpFile+pbfh->offBits;
  1529.   hbm=makebmp(pbih,prgb,pbPelData);
  1530. }
  1531.  
  1532. Function parse() takes two parameters:  a pointer to a BITMAPFILEHEADER2 
  1533. structure and a pointer to the beginning of bitmap file.  For a single bitmap 
  1534. file, these two are the same, but they are different for a multiple bitmap 
  1535. file. 
  1536.  
  1537. The first line locates the bitmap info structure (BITMAPINFOHEADER2).  The 
  1538. second line finds the color table, and the third line finds the pel data.  The 
  1539. last line passes these three values to makebmp(), which actually creates the 
  1540. bitmap and is covered in the next section. 
  1541.  
  1542. Select this to go to the next section 
  1543.  
  1544.  
  1545. ΓòÉΓòÉΓòÉ 2.3.9. Making the Bitmap ΓòÉΓòÉΓòÉ
  1546.  
  1547. Once the three parts of a bitmap are located, they need to be combined into a 
  1548. format acceptable by the GpiCreateBitmap() API.  The following code does just 
  1549. that: 
  1550.  
  1551. #define INCL_WINWINDOWMGR
  1552. #define INCL_GPIBITMAPS
  1553. #define INCL_DEV
  1554. #include <os2.h>
  1555.  
  1556. #include <malloc.h>
  1557. #include <memory.h>
  1558.  
  1559. static int ipow(int b, int e) {
  1560.   int p=b;
  1561.   while (--e) p*=b;
  1562.   return p;
  1563. }
  1564.  
  1565. HBITMAP makebmp(PBITMAPINFOHEADER2 pbih2, PRGB2 prgb2, PBYTE pbPelData) {
  1566.  
  1567. // Determine size of color table
  1568.   int iNumColors,numbits=pbih2->cPlanes * pbih2->cBitCount;
  1569.   if (numbits != 24)
  1570.     iNumColors = pbih2->cclrUsed ? pbih2->cclrUsed : ipow(2,numbits);
  1571.   else
  1572.     iNumColors = pbih2->cclrUsed;
  1573.   int iColorTableSize=iNumColors*sizeof(RGB2);
  1574.  
  1575. // Allocate storage for BITMAPINFO2
  1576.   PBITMAPINFO2 pbi2=(PBITMAPINFO2) malloc(sizeof(BITMAPINFOHEADER2)+iColorTableSize);
  1577.   if (!pbi2) return 0;
  1578.  
  1579.   memcpy(pbi2,pbih2,sizeof(BITMAPINFOHEADER2));                           // Copy First half
  1580.   memcpy((PBYTE) pbi2+sizeof(BITMAPINFOHEADER2),prgb2,iColorTableSize);   //  Second half
  1581.  
  1582.   HPS hps=WinGetPS(HWND_DESKTOP);
  1583.   HBITMAP hbm=GpiCreateBitmap(hps,pbih2,CBM_INIT,pbPelData,pbi2);
  1584.   WinReleasePS(hps);
  1585.   free(pbi2);
  1586.   return hbm;
  1587. }
  1588.  
  1589. Function ipow() computes b^e, which is used to compute the size of the color 
  1590. table, in case the bitmap info structure doesn't specify it. 
  1591.  
  1592. GpiCreateBitmap() doesn't use a BITMAPINFOHEADER2 structure.  Instead, it uses 
  1593. a BITMAPINFO2 structure, which is nothing more than a BITMAPINFOHEADER2 
  1594. followed by a color table. The bitmap file format always places the color table 
  1595. immediately after the corresponding BITMAPINFOHEADER2 structure, so you would 
  1596. think that there is no need to pass a BITMAPINFOHEADER2 and a color table. 
  1597. However, we want makebmp() to be generic enough to take bitmap data that didn't 
  1598. necessarily come from a bitmap file. 
  1599.  
  1600. Function makebmp() performs the following tasks: 
  1601.  
  1602. o Determine the size of the color table 
  1603. o Allocate enough space to hold the BITMAPINFOHEADER2 and the color table 
  1604. o Copy both pieces to this block of data.  This gives you a BITMAPINFO2. 
  1605. o Create a presentation space compatible with the display 
  1606. o Create the bitmap. 
  1607. o Release the presenation space and the allocated memory, since neither is 
  1608.   needed any more. 
  1609.  
  1610. Determining the size of the color table. 
  1611.  
  1612. If the bitmap is not a 24-bit bitmap, then there's a chance that the color 
  1613. table is 2^n entries long.  If the cclrUsed field of the bitmap info structure 
  1614. is zero, then it is assumed that the table is full-length (i.e. 2^n entries 
  1615. long).  Otherwise, there are cclrUsed entries. 
  1616.  
  1617. If it is a 24-bit bitmap, then there cannot be 2^24 entries, so the cclrUsed 
  1618. field is guaranteed to contain the length of the color table.  If this value is 
  1619. zero, then there is no color table. Technically speaking, a 24-bit bitmap 
  1620. doesn't need a color table at all. 
  1621.  
  1622. Allocating the memory. 
  1623.  
  1624. Since a BITMAPINFO2 structure is equivalent to a a BITMAPINFOHEADER2 plus a 
  1625. color table, we need to allocated enough enough space for both.  The size of a 
  1626. bitmap info structure is sizeof(BITMAPINFOHEADER2) [Remember the 
  1627. variable-length characteristic - Editor] and the size of the color table is 
  1628. equal to the number of entries times sizeof(RGB2). 
  1629.  
  1630. Initializing the BITMAPINFO2 block. 
  1631.  
  1632. A pair of memcpy()'s, first for the BITMAPINFOHEADER2 and then for the color 
  1633. table right after it, is all it takes. 
  1634.  
  1635. Creating the presentation space. 
  1636.  
  1637. In order to create a bitmap, OS/2 needs to know where you're planning on using 
  1638. it.  Since we want to display these bitmaps on the screen, we need to create an 
  1639. appropriate presentation space.  The easiest way to do this is to call 
  1640. WinGetPS() and pass it the handle of the desktop window. 
  1641.  
  1642. Creating the bitmap. 
  1643.  
  1644. We now have everything we need.  Just pass all the parameters to 
  1645. GpiCreateBitmap(). 
  1646.  
  1647. Cleanup. 
  1648.  
  1649. The bitmap that we create is self-sufficient, i.e. OS/2 makes a copy of all the 
  1650. data it needs, and the presentation space is only required during the creation, 
  1651. so we can release these two resources.  That's it! 
  1652.  
  1653. Select this to go to the next section 
  1654.  
  1655.  
  1656. ΓòÉΓòÉΓòÉ 2.3.10. Using Multiple Bitmaps ΓòÉΓòÉΓòÉ
  1657.  
  1658. If you've ever edited one of the standard icons that comes with OS/2, you'll 
  1659. notice there are several images defined.  There's one that's 40x40 with 256 
  1660. colors, one that's 32x32 with 16 colors, and several others. When OS/2 displays 
  1661. an icon, it searches the file for the best fit. 
  1662.  
  1663. You can also use the same technique.  Different bitmaps for different 
  1664. resolutions and colors can be created.  OS/2 can scale and dither images 
  1665. automatically, but the results are often unsatisfactory.  By defining a 
  1666. different bitmap for each resolution and color depth, you can always have 
  1667. perfect images. 
  1668.  
  1669. Unfortunately, this approach is error-prone.  Usually you need to define a set 
  1670. of bitmaps that go together.  And for each bitmap in the set, you'll want one 
  1671. for each resolution.  If you create one bitmap for a certain resolution, you'll 
  1672. create to do the same for all other bitmaps.  If you don't, then you risk 
  1673. having your bitmaps mismatched. 
  1674.  
  1675. Alternatively, you could have your program automatically scale the nearest 
  1676. match.  But what is the nearest match?  Which is closer to 800x600: 640x480 or 
  1677. 1024x768?  And what if you're running 800x600 at 256 colors, and you have two 
  1678. bitmaps defined: 800x600x16 colors and 1024x768x256 colors.  Which is more 
  1679. important, the resolution or the number of colors? 
  1680.  
  1681. If I ever write a second edition for this article, I will address this issue in 
  1682. more detail. 
  1683.  
  1684. Select this to go to the next section 
  1685.  
  1686.  
  1687. ΓòÉΓòÉΓòÉ 2.3.11. The BMPINFO program ΓòÉΓòÉΓòÉ
  1688.  
  1689. Included with this article is the source code to BMPINFO - a program which 
  1690. provides a detailed dump of all bit map files, including icons and pointers. 
  1691. This program is useful for testing programs which need to scan or create bitmap 
  1692. files.  It can also be used to get a better understand of the bitmap file 
  1693. format.  It supports single and multiple bitmaps, icons, and pointers in both 
  1694. the new and old bitmap file formats. 
  1695.  
  1696. Note that many programs which create bitmap files might not initialize all the 
  1697. fields correctly.  For instance, versions of Joe View prior to 1.22 would not 
  1698. set the cbSize field correctly, so BMPINFO could not determine the the version 
  1699. of the file format. 
  1700.  
  1701. Select this to go to the next section 
  1702.  
  1703.  
  1704. ΓòÉΓòÉΓòÉ 2.4. Writing a Direct Manipulation Spy ΓòÉΓòÉΓòÉ
  1705.  
  1706. Written by Larry Salomon, Jr. 
  1707.  
  1708. Introduction 
  1709.  
  1710. Almost everyone has at least heard of or even used the development tool PM Spy. 
  1711. The equivalent of the Windows application Spy, it captures all of the messages 
  1712. sent to a window or queue and displays them in a nicely formatted listbox. 
  1713. Some time ago, while adding direct manipulation support to a rather complex 
  1714. application, I realized how difficult it is to actually do this; also, there 
  1715. were more than just a few questions on how to integrate with the Workplace 
  1716. Shell via "drag-n-drop". 
  1717.  
  1718. "Wouldn't it be nice," I thought, "if there were a tool to help people with 
  1719. rendering mechanisms, etc.?"  I concluded that it would be nice, and I finally 
  1720. got the gumption to write one. 
  1721.  
  1722. Drop Me! is a tool that saves all of the information about the items being 
  1723. dragged over its window.  When it receives notification that the drag operation 
  1724. has left the window, it displays the information in a container.  All of the 
  1725. fields in the DRAGITEM structure are saved. 
  1726.  
  1727. When you think about it, this utility is a trivial one.  However, this article 
  1728. is being written to discuss the trials and tribulation of its authoring. 
  1729.  
  1730. Select this to go to the next section 
  1731.  
  1732.  
  1733. ΓòÉΓòÉΓòÉ 2.4.1. Introduction ΓòÉΓòÉΓòÉ
  1734.  
  1735. Written by Larry Salomon, Jr. 
  1736.  
  1737. Almost everyone has at least heard of or even used the development tool PM Spy. 
  1738. The equivalent of the Windows application Spy, it captures all of the messages 
  1739. sent to a window or queue and displays them in a nicely formatted listbox. 
  1740. Some time ago, while adding direct manipulation support to a rather complex 
  1741. application, I realized how difficult it is to actually do this; also, there 
  1742. were more than just a few questions on how to integrate with the Workplace 
  1743. Shell via "drag-n-drop". 
  1744.  
  1745. "Wouldn't it be nice," I thought, "if there were a tool to help people with 
  1746. rendering mechanisms, etc.?"  I concluded that it would be nice, and I finally 
  1747. got the gumption to write one. 
  1748.  
  1749. Drop Me! is a tool that saves all of the information about the items being 
  1750. dragged over its window.  When it receives notification that the drag operation 
  1751. has left the window, it displays the information in a container.  All of the 
  1752. fields in the DRAGITEM structure are saved. 
  1753.  
  1754. When you think about it, this utility is a trivial one.  However, this article 
  1755. is being written to discuss the trials and tribulation of its authoring. 
  1756.  
  1757. Select this to go to the next section 
  1758.  
  1759.  
  1760. ΓòÉΓòÉΓòÉ 2.4.2. Right to the Heart of the Matter ΓòÉΓòÉΓòÉ
  1761.  
  1762. The application is rather straight forward; it is a PM application which 
  1763. creates a "standard window" and within that a container window is created as a 
  1764. child of the client window.  The container is initialized in detail view. 
  1765.  
  1766. Let us consider the events that occur when direct manipulation occurs, from the 
  1767. target's perspective: 
  1768.  
  1769.  1. The user initiates the drag operation 
  1770.  
  1771.  2. The drag moves over the window 
  1772.  
  1773.  3. The user either terminates the drag operation by 
  1774.  
  1775.    a) ...releasing the mouse button to perform the operation 
  1776.    b) ...pressing F1 for direct manipulation help 
  1777.    c) ...pressing ESC to cancel the operation 
  1778.  
  1779.  4. ...or the drag leaves the window 
  1780.  
  1781. From a message standpoint, the application receives a DM_DRAGOVER, DM_DROP, 
  1782. DM_DRAGHELP, or DM_DRAGLEAVE message for steps 2, 3a, 3b, and 4, respectively. 
  1783. Since the client is covered by the container, however, we must instead rely on 
  1784. it to notify us of these events. 
  1785.  
  1786. Fortunately, there are many notifications for direct manipulation provided by 
  1787. the container class.  We are interested specifically in the CN_DRAGOVER and 
  1788. CN_DRAGLEAVE notifications; because we will not be accepting any drop 
  1789. operations, we do not care about the CN_DROP notification. 
  1790.  
  1791. Select this to go to the next section 
  1792.  
  1793.  
  1794. ΓòÉΓòÉΓòÉ 2.4.3. "Doc, this headache is killing me" ΓòÉΓòÉΓòÉ
  1795.  
  1796. Original Intentions 
  1797.  
  1798. Originally, I had implemented the utility in the following manner: when the 
  1799. CN_DRAGOVER message was received, convert each DRAGITEM field to a string and 
  1800. store the resulting structures in a linked list (assuming the list was not 
  1801. already initialized); when the CN_DRAGLEAVE message was received, if the list 
  1802. was not empty, empty the container, add the contents of the linked list to the 
  1803. container, and empty the linked list to prime ourselves for the next drag 
  1804. operation. 
  1805.  
  1806. This was much too complex.  First, it requires that the Common/2 package 
  1807. (containing the linked list routines) be installed on the machine where it is 
  1808. being compiled.  Second, why can we not simply insert the records into the 
  1809. container when we receive the CN_DRAGOVER message? 
  1810.  
  1811. Hmm, good question. 
  1812.  
  1813. Unnecessary Complexities 
  1814.  
  1815. My intent was not to "scare" the user by having this information pop up 
  1816. immediately when the mouse moved over the container.  However, moving the 
  1817. CN_DRAGLEAVE processing to the end of the CN_DRAGOVER processing as a test 
  1818. revealed that the records, while added to the container, do not get displayed 
  1819. because the direct manipulation operation locks the screen until it is 
  1820. completed.  Back to the drawing board. 
  1821.  
  1822. The result is what is included as dropme.zip.  The source no longer requires 
  1823. Common/2, which results in an executable size that has been halved.  Also, the 
  1824. code is less complex, which means it is easier to understand. 
  1825.  
  1826. Select this to go to the next section 
  1827.  
  1828.  
  1829. ΓòÉΓòÉΓòÉ 2.4.4. For the Container Virgins ΓòÉΓòÉΓòÉ
  1830.  
  1831. If you have never done any programming with the container control, I highly 
  1832. recommend you read the "Programming the Container Control" series (3 parts) in 
  1833. issues 3, 4, and 5 of EDM/2.  I will briefly describe the specifics of the 
  1834. interactions with the container here, however. 
  1835.  
  1836. The container is created using WinCreateWindow() in the WM_CREATE processing 
  1837. and is resized in the WM_SIZE processing to insure that it completely covered 
  1838. the client.  The initialization, which also occurs in WM_CREATE, involves 
  1839. allocating the FIELDINFO structures (via a CM_ALLOCDETAILFIELDINFO message), 
  1840. initializing them, and finally inserting them (via a CM_INSERTDETAILFIELDINFO 
  1841. message).  Before exiting, the container is set to "detail view" mode (via a 
  1842. CM_SETCNRINFO message) specifying that the column headings should also be 
  1843. displayed. 
  1844.  
  1845. When the container notifies us that a drag operation is occuring over our 
  1846. window (by sending us a WM_CONTROL message specifying CN_DRAGOVER as the 
  1847. notification code), we allocate enough records for each item being dragged (via 
  1848. a CM_ALLOCRECORD message) and then loop through the items; DrgQueryDragitem() 
  1849. is called for each and the contents of the DRAGITEM structure are converted to 
  1850. strings to be displayed in the container.  Finally, the current contents are 
  1851. emptied (via a CM_REMOVERECORD message) and the new records are inserted (via a 
  1852. CM_INSERTRECORD message). 
  1853.  
  1854. Select this to go to the next section 
  1855.  
  1856.  
  1857. ΓòÉΓòÉΓòÉ 2.4.5. Possible Enhancements ΓòÉΓòÉΓòÉ
  1858.  
  1859. Now that we have our nifty utility, it would be nice if it had the ability 
  1860. to... 
  1861.  
  1862. o ...copy one or more records to the clipboard. 
  1863. o ...save the contents of the container to a file. 
  1864.  
  1865. The first item should not be difficult and, using the article "Threads in PM 
  1866. Applications" from last month's issue of EDM/2, the second item should not 
  1867. require too much time to implement either.  These exercises are left to the 
  1868. reader. 
  1869.  
  1870. Select this to go to the next section 
  1871.  
  1872.  
  1873. ΓòÉΓòÉΓòÉ 2.4.6. Epilogue ΓòÉΓòÉΓòÉ
  1874.  
  1875. Here is the "Reader's Digest Condensed" version of a humorous incident that I 
  1876. had while writing this application.  I would like to thank my good friend Peter 
  1877. Haggar (the original container developer) for taking time out of his busy 
  1878. schedule to give me a much-needed hand in debugging. 
  1879.  
  1880. "Hi Peter.  This is Larry Salomon." 
  1881.  
  1882. "Hey Larry.  How are you?" 
  1883.  
  1884. "Can't complain much.  Yourself?" 
  1885.  
  1886. "Not too bad.  What can I do for you?" 
  1887.  
  1888. "Well, I have the problem.  It seems that when I try to insert some records 
  1889. into the container during a CN_DRAGLEAVE message, my application traps.  I have 
  1890. checked everything in the insertion procedure and it looks fine.  Is this some 
  1891. sort of critical section that I need to be aware of?" 
  1892.  
  1893. [Tap tap tap] (He brings up the source code) 
  1894.  
  1895. "Well, it seems that we do check to see if a drag operation is in progress and 
  1896. call the appropriate function to get the HPS since the screen is locked." 
  1897.  
  1898. [The rest of the "debugging over the phone" is deleted] 
  1899.  
  1900. [The next morning] 
  1901.  
  1902. "Hi Peter; it's me again." 
  1903.  
  1904. "Hey." 
  1905.  
  1906. "As I was driving home last night, I thought that because I'm not using the 
  1907. pszIcon field that the container might still be trying to access it and that is 
  1908. causing the trap.  However, that is not the case.  Here is everything that my 
  1909. application does.  First, it allocates the FIELDINFO structures and inserts 
  1910. them into the container.  I'm using CFA_STRING because I know the DRAGITEM will 
  1911. probably not be valid anymore.  And then I..." 
  1912.  
  1913. "Are your field offsets pointing to a pointer to the string?" 
  1914.  
  1915. "Oh, shit..." 
  1916.  
  1917. "Yeah, we should've named the constant CFA_PSZ; it confuses everyone.  By the 
  1918. way, isn't this [pitfall] in your book?" 
  1919.  
  1920. "Oh, shit.  How embarrassing..." 
  1921.  
  1922. ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ 
  1923.  
  1924. See you next time. 
  1925.  
  1926. Select this to go to the next section 
  1927.  
  1928.  
  1929. ΓòÉΓòÉΓòÉ 3. Columns ΓòÉΓòÉΓòÉ
  1930.  
  1931. The following columns can be found in this issue: 
  1932.  
  1933. o Introduction to PM Programming 
  1934. o Scratch Patch 
  1935.  
  1936.  
  1937. ΓòÉΓòÉΓòÉ 3.1. Introduction to PM Programming ΓòÉΓòÉΓòÉ
  1938.  
  1939. Written by Larry Salomon, Jr. 
  1940.  
  1941. Introduction 
  1942.  
  1943. The purpose of this column is to provide to the readers out there who are not 
  1944. familiar with PM application development the information necessary to satisfy 
  1945. their curiousity, educate themselves, and give them an advantage over the 
  1946. documentation supplied by IBM.  Of course, much of this stuff could probably be 
  1947. found in one of the many books out there, but the problem with books in general 
  1948. is that they don't answer the questions you have after you read the book the 
  1949. first time through. 
  1950.  
  1951. I will gladly entertain feedback from the readers about what was "glossed over" 
  1952. or what was detailed well, what tangential topics need to be covered and what 
  1953. superfluous crap should have been removed.  This feedback is essential in 
  1954. guaranteeing that you get what you pay for.  :) 
  1955.  
  1956. It should be said that you must not depend solely on this column to teach you 
  1957. how to develop PM applications; instead, this should be viewed as a supplement 
  1958. to your other information storehouses (books, the network conferences, etc.). 
  1959. Because this column must take a general approach, there will be some topics 
  1960. that would like to see discussed that really do not belong here.  Specific 
  1961. questions can be directed to the Scratch Patch, where an attempt to answer them 
  1962. will be made. 
  1963.  
  1964. Select this to go to the next section 
  1965.  
  1966.  
  1967. ΓòÉΓòÉΓòÉ 3.1.1. Introduction ΓòÉΓòÉΓòÉ
  1968.  
  1969. Written by Larry Salomon, Jr. 
  1970.  
  1971. The purpose of this column is to provide to the readers out there who are not 
  1972. familiar with PM application development the information necessary to satisfy 
  1973. their curiousity, educate themselves, and give them an advantage over the 
  1974. documentation supplied by IBM.  Of course, much of this stuff could probably be 
  1975. found in one of the many books out there, but the problem with books in general 
  1976. is that they don't answer the questions you have after you read the book the 
  1977. first time through. 
  1978.  
  1979. I will gladly entertain feedback from the readers about what was "glossed over" 
  1980. or what was detailed well, what tangential topics need to be covered and what 
  1981. superfluous crap should have been removed.  This feedback is essential in 
  1982. guaranteeing that you get what you pay for.  :) 
  1983.  
  1984. It should be said that you must not depend solely on this column to teach you 
  1985. how to develop PM applications; instead, this should be viewed as a supplement 
  1986. to your other information storehouses (books, the network conferences, etc.). 
  1987. Because this column must take a general approach, there will be some topics 
  1988. that would like to see discussed that really do not belong here.  Specific 
  1989. questions can be directed to the Scratch Patch, where an attempt to answer them 
  1990. will be made. 
  1991.  
  1992. Select this to go to the next section 
  1993.  
  1994.  
  1995. ΓòÉΓòÉΓòÉ 3.1.2. What is PM? ΓòÉΓòÉΓòÉ
  1996.  
  1997. PM - an acronym for Presentation Manager - was originally a subsystem intended 
  1998. to provide a graphical user environment to make the system easier to use. 
  1999. Since OS/2 version 1.1 when it was introduced, it has grown to become more of a 
  2000. Siamese twin, joined at the hip.  If viewed as a set of components, it can be 
  2001. broken down into the following: 
  2002.  
  2003. o Device functions 
  2004. o Direct manipulation functions 
  2005. o Graphics functions 
  2006. o Hooks 
  2007. o Message processing 
  2008. o Spooler functions 
  2009. o Window functions 
  2010. o Workplace Shell classes 
  2011.  
  2012. Figure 1) The PM components as listed in the Technical Reference 
  2013.  
  2014. An explaination of the components is provided below.  The prefix used on the 
  2015. functions belonging to the groups is listed after the component name, when one 
  2016. exists. 
  2017.  
  2018. Device functions (Dev) - since OS/2 takes the outlook that all output devices 
  2019. are logical devices, this component is used to access and query the physical 
  2020. device  that the application is using. 
  2021.  
  2022. Direct Manipulation functions (Drg) - commonly referred to as "drag-n-drop", 
  2023. these functions implement a common protocol for communicating what objects are 
  2024. being dragged from one window to another. 
  2025.  
  2026. Graphics functions (Gpi) - allows an application to draw on any output device, 
  2027. in addition to providing a host of other functions. 
  2028.  
  2029. Hooks - allow an application to intercept notification of and act on certain 
  2030. types of events before the intended recipient is receives notification. 
  2031.  
  2032. Message processing - communicates an event to an application. 
  2033.  
  2034. Spooler functions (Spl) - provides printer queue management functions. 
  2035.  
  2036. Window functions (Win) - this is a bit of a misnomer, because there are 
  2037. functions included in this group which really do not have a direct relationship 
  2038. with windows. 
  2039.  
  2040. Workplace Shell classes - included in OS/2 2.x, this component provides the 
  2041. System Object Model (SOM) classes that the user-interface uses, such as 
  2042. folders, palettes (color, font, and scheme), etc. 
  2043.  
  2044. We will be concentrating on the two components that you cannot avoid - Message 
  2045. processing and Window functions. 
  2046.  
  2047. Select this to go to the next section 
  2048.  
  2049.  
  2050. ΓòÉΓòÉΓòÉ 3.1.3. Architecture 101 ΓòÉΓòÉΓòÉ
  2051.  
  2052. Before we can go on, we need to step back and look at the 1000-foot view of PM 
  2053. "things", after which we can start defining the terms you have already been 
  2054. confused by. 
  2055.  
  2056. Object Oriented? 
  2057.  
  2058. Disclaimer - I am not a hard-core C++ programmer, so my use of terms used in 
  2059. object oriented books might be incorrect.  As Maddie, the moderator of 
  2060. rec.humor.funny so blithly put it:  you can get a refund of double what you 
  2061. paid for this.  :) 
  2062.  
  2063. PM has a half-implemented object oriented design, which hopefully some of you 
  2064. will find familiar; the exception to this is the Workplace Shell classes which, 
  2065. although a part of PM, are completely object oriented because they are based on 
  2066. SOM.  The half-implemented design can be described as such: pick a object, 
  2067. without thinking about specifics.  Let's say a toaster. Now, consider that all 
  2068. toasters can 
  2069.  
  2070. o Toaster bread 
  2071. o Allow you to set the darkness of the toast 
  2072. o "Tell you" the darkness setting (by looking at it) 
  2073. o Insert bread 
  2074. o Remove toast 
  2075.  
  2076. Figure 2) Characteristics of a toaster 
  2077.  
  2078. This is the concept of a toaster, but not the toaster itself. For that, you 
  2079. must go to your local K-Mart, Caldor, etc. and buy a toaster. This toaster or 
  2080. is said to be an instance of the toaster concept in reality.  An important 
  2081. distinction here is that the concept defines the characteristics, but not the 
  2082. way the characteristics are defined.  In other words, each instance of a 
  2083. toaster has different ways of accomplishing the tasks above, but they all can 
  2084. accomplish the tasks in some manner.  An example might be that one toaster 
  2085. receives bread through the top, while another has a front door that you open to 
  2086. insert the bread. 
  2087.  
  2088. Figure 3) Relationship between concept and reality. 
  2089.  
  2090. The concept in PM is the window class and the instance of the concept is the 
  2091. window.  A window is defined to be a rectangular area in a Cartesian coordinate 
  2092. space, and has the ability to paint itself, be sized, be moved, etc. 
  2093.  
  2094. It's a Fashion Thing 
  2095.  
  2096. When you buy a automotive vehicle, you can specify that you want a radio, 
  2097. four-wheel drive, air conditioning; in PM the application can specify a window 
  2098. style, which controls (to an extent) how the window reacts/behaves in specific 
  2099. situations.  These window styles can be specified when the window is created 
  2100. or, if every window of the class has a common set of styles, these can be 
  2101. specified when the class is defined and are then called class styles. 
  2102.  
  2103. Figure 4) Relationship between window class and window objects. 
  2104.  
  2105. Select this to go to the next section 
  2106.  
  2107.  
  2108. ΓòÉΓòÉΓòÉ 3.1.4. Events ΓòÉΓòÉΓòÉ
  2109.  
  2110. An event is the most important entity in PM application development; it has the 
  2111. same definition here as it does in the "real world".  Events occur for any of a 
  2112. myriad of reasons - the mouse moved, a menu item was selected, a window was 
  2113. destroyed, etc. - and the application is notified by the system using a 
  2114. message. 
  2115.  
  2116. (What an abstract term.  I remember reading about messages for the first time 
  2117. eons ago and wondering what the hell the term "message" meant.) 
  2118.  
  2119. A message is really no more than a ULONG sent to a function in your 
  2120. application.  Each message can have up to eight parameters (all characters), 
  2121. and can optionally return a result.  Additionally, an application not only 
  2122. receives messages, but it can also send messages.  For system-defined messages, 
  2123. there are constants defined in pmwin.h, and there numerical values are in the 
  2124. range 1-(WM_USER-1).  Any application-specific messages (user messages) should 
  2125. be defined as some value greater than or equal to the system constant WM_USER. 
  2126.  
  2127. A message's parameters are packed into a special type - MPARAM - and a 
  2128. message's result is packed into another type - MRESULT.  From either, the 
  2129. various parameters can be extracted using one of the many "helper macros" 
  2130. defined. 
  2131.  
  2132. MPARAM mpParm1;
  2133. MRESULT mrResult;
  2134.  
  2135. datatypeFROMMP(mpParm1)
  2136. datatypeFROMMR(mrResult)
  2137.  
  2138. Figure 5) Syntax of the MPARAM/MRESULT extraction macro names 
  2139.  
  2140. In figure 5, datatype can be one of the following: 
  2141.  
  2142. PVOID               Pointer to a VOID which should be cast to the appropriate 
  2143.                     type. 
  2144. HWND                Window handle (see the next section) 
  2145. CHAR1               The first character (bits 0-7). 
  2146. CHAR2               The first character (bits 8-15). 
  2147. CHAR3               The first character (bits 16-23). 
  2148. CHAR4               The first character (bits 23-31). 
  2149. SHORT1              The first USHORT (bits 0-15). 
  2150. SHORT2              The second USHORT (bits 16-31). 
  2151. LONG                A ULONG. 
  2152.  
  2153. Select this to go to the next section 
  2154.  
  2155.  
  2156. ΓòÉΓòÉΓòÉ 3.1.5. Resources and Handles ΓòÉΓòÉΓòÉ
  2157.  
  2158. In addition to windows, PM manages a lot of many other different things: 
  2159. icons, bitmaps, strings, menus, drawing boards (known as presentation spaces), 
  2160. etc.  The management of these entities - known as resources - is assisted by 
  2161. many internal tables that we do not care about (other than for curiousity's 
  2162. sake maybe).  Each resource is referred to by a handle, which is nothing more 
  2163. than a ULONG used as an index into one or more tables. 
  2164.  
  2165. The importance of this point is that, because these resource handles are 
  2166. typedef'd from a ULONG, the ability to specify the wrong type of handle as a 
  2167. parameter to a function is substantially increased.  While the temptation to 
  2168. typecast the parameter to avoid any warnings or errors generated by your 
  2169. compiler is great, care must be exercised to insure that what you are doing 
  2170. agrees with the original intent of the function-resource relationship. 
  2171.  
  2172. As an example, a pointer and an icon are two similar resources, yet there is a 
  2173. WinLoadPointer() function and no WinLoadIcon() function.  While few people know 
  2174. why, everyone knows that you can use WinLoadPointer() to load an icon (from the 
  2175. resource table, a concept that will be discussed in future issues.  This is 
  2176. noted so that you do not think that it loads from a separate disk file, which 
  2177. it does not.).  You cannot, however, use WinLoadPointer() to load a bitmap from 
  2178. the resource table, because pointers and bitmaps have fundamental differences 
  2179. in their purposes. 
  2180.  
  2181. One final note:  a NULL handle is specified using the constant NULLHANDLE and 
  2182. not using NULL.  Though you can cast NULL to the appropriate type, why type the 
  2183. extra keystrokes? 
  2184.  
  2185. Select this to go to the next section 
  2186.  
  2187.  
  2188. ΓòÉΓòÉΓòÉ 3.1.6. Putting it Together ΓòÉΓòÉΓòÉ
  2189.  
  2190. Now that I've managed to confuse you with what seems to be a bunch of distantly 
  2191. related concepts, let us take a better look at windows and window classes to 
  2192. bind it together. 
  2193.  
  2194. A window class is defined to be an entity that consists of the following: 
  2195.  
  2196. o A name (string) 
  2197. o Zero or more class-styles (separate bits in a ULONG) 
  2198. o A window class procedure (usually incorrectly referred to as a window 
  2199.   procedure) 
  2200. o Zero or more bytes of memory that is allocated for each window of the class 
  2201.   (called window words) 
  2202.  
  2203. The class name is bound to the remainder of the class parameters, and is used 
  2204. as a referencing index.  Thus, when you create windows of a particular class, 
  2205. you need specify only the class name and the system performs a table lookup to 
  2206. retrieve the specifics. 
  2207.  
  2208. The window class procedure which receives all of the events directed at a 
  2209. window of the class.  Its prototype is defined below: 
  2210.  
  2211. MRESULT EXPENTRY myProcedure(HWND hwndWnd,
  2212.                              ULONG ulMsg,
  2213.                              MPARAM mpParm1,
  2214.                              MPARAM mpParm2);
  2215.  
  2216. Figure 6) Prototype of a window class procedure 
  2217.  
  2218. The parameters should look familiar except for one. 
  2219.  
  2220. hwndWnd             The handle of the window receiving the message. 
  2221. ulMsg               The message identifier being sent. 
  2222. mpParm1             Message parameter 1. 
  2223. mpParm2             Message parameter 2. 
  2224.  
  2225. This function only processes the messages it is interested in (typically done 
  2226. by the C "switch" construct).  The other messages are handled by passing them 
  2227. to a special system function - WinDefWindowProc() - and returning its result. 
  2228.  
  2229. The window words are important concept that cannot be overlooked...so we won't 
  2230. overlook them.  The idea is that n bytes of memory are allocated for every 
  2231. window of the class that is created. This memory can be accessed with the 
  2232. functions WinQueryWindowULong() and WinSetWindowULong() (specifying the 
  2233. constant QWL_USER), and their functional equivalents WinQueryWindowPtr() and 
  2234. WinSetWindowPtr(). Note that since the number of bytes to be queried/set is a 
  2235. multiple of 4, a multiple of 4 should be specified when the class is created. 
  2236.  
  2237. What is the point(er) of using window words?  By storing a pointer to an 
  2238. application-defined structure (where the structure is dynamically allocated 
  2239. using malloc()/calloc()), you can maintain a separate set of data for each 
  2240. window of the class.  See the article "Development of a New Window Class - Part 
  2241. 1" in issue 4 of EDM/2 for more information on window words. 
  2242.  
  2243. One last question: how is the class created?  The class is created (said to be 
  2244. registered) using the WinRegisterClass() function, which we will see more of in 
  2245. future issues. 
  2246.  
  2247. Select this to go to the next section 
  2248.  
  2249.  
  2250. ΓòÉΓòÉΓòÉ 3.1.7. Summary ΓòÉΓòÉΓòÉ
  2251.  
  2252. In this issue, we have wiped the slate clean and started anew.  We should have 
  2253. learned... 
  2254.  
  2255. o ...what the components of Presentation Manager are. 
  2256. o ...the relationship of an object class to an object instance and how windows 
  2257.   fit into this view of the world. 
  2258. o ...the purpose of window styles and class styles and the difference between 
  2259.   the two. 
  2260. o ...what an event, an MPARAM, and an MRESULT is. 
  2261. o ...what resources are and how an application refers to them. 
  2262. o ...what the components of a class are. 
  2263. o ...what window words are and why they are used. 
  2264.  
  2265. Next month, we will build our "first" PM application and take it apart to see 
  2266. what lurks beneath the surface. 
  2267.  
  2268. Select this to go to the next section 
  2269.  
  2270.  
  2271. ΓòÉΓòÉΓòÉ 3.2. Scratch Patch ΓòÉΓòÉΓòÉ
  2272.  
  2273. Written by Larry Salomon, Jr. 
  2274.  
  2275. Welcome to this month's "Scratch Patch"!  Each month, I collect various items 
  2276. that fit into this column sent to me via email. The ones that I feel contribute 
  2277. the most to developers, whether in terms of information or as a nifty trick to 
  2278. tuck into your cap, get published in this column. 
  2279.  
  2280. To submit an item, send it via email to my address - os2man@panix.com - and be 
  2281. sure to grant permission to publish it (those that forget will not be 
  2282. considered for publication).  This month, we have the following: 
  2283.  
  2284. o Corrections 
  2285. o Questions and Answers 
  2286. o Snippet(s) of the Month 
  2287. o Documentation Chop Shop 
  2288. o Want Ads 
  2289.  
  2290.  
  2291. ΓòÉΓòÉΓòÉ 3.2.1. Corrections ΓòÉΓòÉΓòÉ
  2292.  
  2293. Since I meant to mention this last month but forgot, I have a correction for 
  2294. the "Help Manager and Online Documentation" article in the October issue.  It 
  2295. is really not a correction, but instead a better way of doing message box help. 
  2296. The idea is to create a dummy HELPSUBTABLE and then for each message box help 
  2297. id, create a HELPITEM whose first and third identifiers are the message box 
  2298. help id, and the second identifier is of the dummy HELPSUBTABLE.  The idea is 
  2299. that when the user requests help, the HELPSUBTABLE will not have the 
  2300. appropriate entry and so the Extended Help panel (the third parameter) will be 
  2301. displayed. 
  2302.  
  2303. The advantage of this is that of performance; hooks are performance eaters, so 
  2304. this is a nice reason to do this (according to Scott Kliger, the developer of 
  2305. the original Help Manager code).  The disadvantage of this is that for larger 
  2306. applications with large numbers of different message boxes, the HELPTABLE can 
  2307. get rather unwieldy. 
  2308.  
  2309. Another clarification is about last month's "Threads in PM Applications" 
  2310. article, where the call to DosSleep() was made in the processing of the 
  2311. MYM_ENDTHREAD message.  The use of this function within a PM application is 
  2312. strongly discouraged because it can lock the system up until the call returns, 
  2313. but if used with caution will cause no noticeable effect.  This case is just 
  2314. that; the thread is already dying, so the loop with the DosSleep() body will 
  2315. execute a minimal number of times. 
  2316.  
  2317. Thanks go to Michael Hohner (fn0728@cd4680fs.rrze.uni-erlangen.de) for sending 
  2318. us both of these items. 
  2319.  
  2320. ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ 
  2321.  
  2322. Also, in issue 5, I stated that, for user painted buttons, BDS_DEFAULT was 
  2323. incorrectly defined.  Instead, it is correctly defined and I was 
  2324. misinterpreting the definition of default. Default in this sense does not mean 
  2325. "paint in the default manner", but instead means that the button is the default 
  2326. button and should be drawn with a darker border.  This is a flag that can be 
  2327. specified in addition to the others (BDS_HILITED and BDS_DISABLED) which are 
  2328. mutually exclusive of each other. 
  2329.  
  2330. A value of 0 can still be received for fsState (in the USERBUTTON structure, so 
  2331. a new constant should be defined for this value (I named it BDS_NORMAL). 
  2332.  
  2333. Select this to go to the next section 
  2334.  
  2335.  
  2336. ΓòÉΓòÉΓòÉ 3.2.2. Questions and Answers ΓòÉΓòÉΓòÉ
  2337.  
  2338. Gordon W. Zeglinski (zeglins@cc.umanitoba.ca) writes: 
  2339.  
  2340. Here's a Q that may make good EDM/2 material.  I want to define a menu in 
  2341. memory, and then create it using WinCreateMenu.  This will eventually be a 
  2342. submenu.  It is impossible to create the submenu in resource files, as its 
  2343. contents are not know until runtime.  The reason I want to create the whole 
  2344. submenu at once instead of placing them the entries into an existing submenu is 
  2345. because placing 18 entries into the sub menu is taking about 1 min on a 486 
  2346. DX33.  A very slow process :(. 
  2347.  
  2348. Anyways, I digress... 
  2349.  
  2350. The documentation states that the WinCreateMenu item takes a Binary Menu 
  2351. template.  After searching through much IBM documentation, I find no such 
  2352. binary template documented... 
  2353.  
  2354. So what is the format of this template ? 
  2355.  
  2356. Gordon 
  2357.  
  2358. (Before everyone gasps at the "1 minute on a 486/33" statement, let me add that 
  2359. Gordon subsequently found that his code was trying to delete several thousand 
  2360. non-existant menu items first, which was causing the severe time delay.) 
  2361.  
  2362. In order to answer the question, one first needs to look at the data structures 
  2363. involved.  In pmwin.h, there are two little-known data structures known as MTI 
  2364. (none) and MT (LMTI), (the datatype names for pointers to these structures are 
  2365. shown in paratheses) which are acronyms for menu template item and menu 
  2366. template. 
  2367.  
  2368. typedef struct _mti {
  2369.    USHORT afStyle;
  2370.    USHORT pad;
  2371.    USHORT idItem;
  2372.    CHAR c[2];
  2373. } MTI;
  2374.  
  2375. typedef struct _mt {
  2376.    ULONG len;
  2377.    USHORT codepage;
  2378.    USHORT reserved;
  2379.    USHORT cMti;
  2380.    MTI rgMti[1];
  2381. } MT, *LPMT;
  2382.  
  2383. The MTI structure fields are explained below: 
  2384.  
  2385. afStyle             one or more MIS_* constants 
  2386. pad                 reserved and must be zero. 
  2387. idItem              menu item identifier. 
  2388. c                   variable length data depending on which bits in afStyle are 
  2389.                     set: 
  2390.  
  2391.    MIS_TEXT            specifies the NULL-terminated menu item text 
  2392.    MIS_BITMAP          If the first byte is '#', the remaining bytes specify 
  2393.                        the ASCII representation of the bitmap resource.  If the 
  2394.                        first byte is zero, then the next four bytes define a 
  2395.                        bitmap handle that has already been loaded. Otherwise, 
  2396.                        there is no bitmap handle associated and the application 
  2397.                        must set one using the MM_SETITEM message. 
  2398.    MIS_OWNERDRAW, MIS_SEPARATOR the data is ignored 
  2399.    MIS_SUBMENU         the data is a menu template 
  2400.  
  2401. The MT structure fields are explained below: 
  2402.  
  2403. len                 specifies the size of the menu template in bytes 
  2404. codepage            specifies the code page to be used 
  2405. reserved            reserved and must be zero. 
  2406. cMti                specifies the number of MTI structures in rgMti 
  2407. rgMti               specifies the MTI structures that comprise the menu items. 
  2408.  
  2409. As you can see, the menu template size will - in all likelihood - have to be 
  2410. calculated at run-time as well, making this a nasty section of code.  Once you 
  2411. have calculated this, allocated the memory, and initialized the structures, you 
  2412. then do one of two things:  if a top-level menu does not already exist, you 
  2413. call WinCreateMenu() and you are done; otherwise, you call WinCreateWindow() to 
  2414. create the submenu as in the following example: 
  2415.  
  2416. hwndSubMenu=WinCreateWindow(HWND_DESKTOP,       // parent
  2417.                             WC_MENU,            // window class
  2418.                             "",         // no text
  2419.                             0,                  // no style :)
  2420.                             0,                  // no position
  2421.                             0,                  //   :
  2422.                             0,                  // nor size
  2423.                             0,                  //   :
  2424.                             HWND_DESKTOP,       // owner
  2425.                             HWND_TOP,           // z-order
  2426.                             M_TESTMENU,         // window id
  2427.                             pmtMenu,            // pCreateParms=menu template
  2428.                             NULL);              // no pres params
  2429.  
  2430. After this, you need to insert the top-level item representing the pulldown by 
  2431. sending the menu control a MM_INSERTITEM message.  The hwndSubMenu field of the 
  2432. MENUITEM structure gets the value returned by the WinCreateWindow() call. 
  2433.  
  2434. miSubMenu.iPosition=MIT_END;
  2435. miSubMenu.afStyle=MIS_SUBMENU|MIS_TEXT;
  2436. miSubMenu.afAttribute=0;
  2437. miSubMenu.id=M_TESTMENU;
  2438. miSubMenu.hwndSubMenu=hwndSubMenu;
  2439. miSubMenu.hItem=NULLHANDLE;
  2440.  
  2441. strcpy(achText,"~Test");
  2442.  
  2443. WinSendMsg(hwndMenu,
  2444.            MM_INSERTITEM,
  2445.            MPFROMP(&miSubMenu),
  2446.            MPFROMP(achText));
  2447.  
  2448. Note that the value of miSubMenu.id is the same as the id specified on the 
  2449. WinCreateWindow() call. 
  2450.  
  2451. Obviously, using menu templates can be a real headache, and the headache gets 
  2452. worse if you have "pull-right" menus.  It might be better for you to determine 
  2453. where your performance bottleneck is in your current code and try to optimize 
  2454. that if possible. 
  2455.  
  2456. A sample program illustrating these concepts has been provided as MENUTEST.ZIP. 
  2457.  
  2458. Select this to go to the next section 
  2459.  
  2460.  
  2461. ΓòÉΓòÉΓòÉ 3.2.3. Snippet(s) of the Month ΓòÉΓòÉΓòÉ
  2462.  
  2463. (Sigh) There was only one submission this month, but unfortunately the person 
  2464. who sent it to me did not write it; remember, you must be the author and you 
  2465. must give me permission to publish your code! 
  2466.  
  2467. Even with this setback, I cannot admit defeat, so I have once again reached 
  2468. down into my black hat and pulled out two useful functions.  As before, all of 
  2469. these are located in the file snippet.zip. 
  2470.  
  2471. BOOL splitFilename(PCHAR pchFile,PCHAR pchDrive,PCHAR pchPath,PCHAR pchName)
  2472. //-------------------------------------------------------------------------
  2473. // This function splits a filename into the drive, path, and name components.
  2474. // This is the functional equivalent of the MSC 6.0 function _splitpath().
  2475. //
  2476. // Input:  pchFile - points to the filename to split
  2477. // Output:  pchDrive - if not NULL, points to the buffer which receives
  2478. //                     the drive portion of the filename
  2479. //          pchPath - if not NULL, points to the buffer which receives
  2480. //                    the path portion of the filename
  2481. //          pchName - if not NULL, points to the buffer which receives
  2482. //                    the name portion of the filename
  2483. // Returns:  TRUE if successful, FALSE otherwise
  2484. //-------------------------------------------------------------------------
  2485. {
  2486.    PCHAR pchBegin;
  2487.    PCHAR pchEnd;
  2488.  
  2489.    pchBegin=pchFile;
  2490.    pchEnd=strchr(pchBegin,':');
  2491.  
  2492.    if (pchDrive!=NULL) {
  2493.       *pchDrive=0;
  2494.  
  2495.       if (pchEnd!=NULL) {
  2496.          strncat(pchDrive,pchBegin,pchEnd-pchBegin+1);
  2497.       } /* endif */
  2498.    } /* endif */
  2499.  
  2500.    if (pchEnd!=NULL) {
  2501.       pchBegin=pchEnd+1;
  2502.    } /* endif */
  2503.  
  2504.    pchEnd=strrchr(pchBegin,'\\');
  2505.  
  2506.    if (pchPath!=NULL) {
  2507.       *pchPath=0;
  2508.  
  2509.       if (pchEnd!=NULL) {
  2510.          strncat(pchPath,pchBegin,pchEnd-pchBegin+1);
  2511.       } /* endif */
  2512.    } /* endif */
  2513.  
  2514.    if (pchEnd!=NULL) {
  2515.       pchBegin=pchEnd+1;
  2516.    } /* endif */
  2517.  
  2518.    if (pchName!=NULL) {
  2519.       strcpy(pchName,pchBegin);
  2520.    } /* endif */
  2521.  
  2522.    return TRUE;
  2523. }
  2524.  
  2525. BOOL isWindowOfClass(HWND hwndWnd,PCHAR pchClass)
  2526. //-------------------------------------------------------------------------
  2527. // This function determines whether or not the specified window is of
  2528. // the specified class.  This will work for either private classes
  2529. // (registered with WinRegisterClass) or the public classes (specified
  2530. // by a WC_* constant).
  2531. //
  2532. // Input:  hwndWnd - specifies the window handle.
  2533. //         pchClass - points to either the window class or specifies a
  2534. //                    WC_* constant to compare with.
  2535. // Returns:  TRUE if hwndWnd is of class pchClass, FALSE otherwise.
  2536. //-------------------------------------------------------------------------
  2537. {
  2538.    CHAR achClass[256];
  2539.    ATOM aAtom;
  2540.  
  2541.    //----------------------------------------------------------------------
  2542.    // For the WC_* classes, WinQueryClassName returns the string
  2543.    // representation of the integer atom as stored in the system atom
  2544.    // table.  The string representation of *any* integer atom is expressed
  2545.    // in the form "#ddddd", where 'ddddd' are the decimal digits specifying
  2546.    // the value of the atom.
  2547.    //
  2548.    // We can check the first character of the class name returned to see
  2549.    // if the window is of a predefined type.
  2550.    //----------------------------------------------------------------------
  2551.    WinQueryClassName(hwndWnd,sizeof(achClass),achClass);
  2552.  
  2553.    if (achClass[0]=='#') {
  2554.       //-------------------------------------------------------------------
  2555.       // We have a WC_* window, so check to see if pchClass is also a
  2556.       // predefined class.  If so, the high word is 0xFFFF.
  2557.       //-------------------------------------------------------------------
  2558.       if (HIUSHORT(pchClass)!=(USHORT)0xFFFF) {
  2559.          return FALSE;
  2560.       } /* endif */
  2561.  
  2562.       //-------------------------------------------------------------------
  2563.       // Get the integer representation of the atom and compare it
  2564.       // against the low word of pchClass, which is also the integer
  2565.       // representation of the class it represents.
  2566.       //-------------------------------------------------------------------
  2567.       aAtom=WinFindAtom(WinQuerySystemAtomTable(),achClass);
  2568.       if (aAtom==LOUSHORT(pchClass)) {
  2569.          return TRUE;
  2570.       } /* endif */
  2571.    } else {
  2572.       //-------------------------------------------------------------------
  2573.       // We don't have a WC_* window, so check to see if pchClass is not
  2574.       // also.  If it isn't, compare the classes using strcmp().
  2575.       //-------------------------------------------------------------------
  2576.       if (HIUSHORT(pchClass)==(USHORT)0xFFFF) {
  2577.          return FALSE;
  2578.       } /* endif */
  2579.  
  2580.       if (strcmp(achClass,pchClass)==0) {
  2581.          return TRUE;
  2582.       } /* endif */
  2583.    } /* endif */
  2584.  
  2585.    return FALSE;
  2586. }
  2587.  
  2588. Select this to go to the next section 
  2589.  
  2590.  
  2591. ΓòÉΓòÉΓòÉ 3.2.4. Documentation Chop Shop ΓòÉΓòÉΓòÉ
  2592.  
  2593. A few of you may have noticed, but in the OS/2 2.1 Toolkit, IBM once again 
  2594. forgot to define the container record attribute CRA_SOURCE.  It is wise to 
  2595. define it directly in the pmstddlg.h file with the value 0x00004000L. 
  2596.  
  2597. ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ 
  2598.  
  2599. When we received our copy of the new C-Set++ compiler, I installed it and 
  2600. immediately proceeded to test for a successful installation by writing a C++ 
  2601. style "Hello world" program.  After many unsuccessful attempts to get the 
  2602. program to link successfully, I was told that the compiler does not insert the 
  2603. default library object record for the C++ class libraries and that the user 
  2604. must do so explicitly.  This is not stated in the documentation; the libraries 
  2605. are listed below: 
  2606.  
  2607. o DDE4MUIB.LIB, DDE4MUIC.LIB, and DDE4MUID.LIB for the standard and 
  2608.   user-interface class libraries, statically linked. 
  2609. o DDE4CC for the container class libraries, statically linked. 
  2610.  
  2611. It should be noted that statically linking these libraries ballooned the "Hello 
  2612. world" application to 131584 bytes!  It is thus recommended that you use the 
  2613. dynamically linked run-time (DDE4MUII.LIB for the standard/user-interface class 
  2614. libraries and DDE4CCI.LIB for the container class libraries) and distribute the 
  2615. DLL's (which are royalty free, but require that you agree to the conditions 
  2616. listed on page 12 of the "C/C++ Tools License Information" pamphlet) with your 
  2617. application. 
  2618.  
  2619. ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ 
  2620.  
  2621. In issue 2, I described how dropping on a printer does not send you a pointer 
  2622. to the DRAGINFO structure, but instead to a DRAGITEM structure corresponding to 
  2623. one of the items dropped on the printer sending the message.  Well, it appears 
  2624. that, in OS/2 2.1, the system forgets to grant you access to the structure (if 
  2625. you think about it, however, this makes more sense than before, but they should 
  2626. still pass the PDRAGINFO structure instead).  How do you access the data? 
  2627. Well, fortunately, the DrgAccessDraginfo() function is not picky about whether 
  2628. or not its argument is a PDRAGINFO or a PDRAGITEM, so insure that you call this 
  2629. function before trying to access the structure's contents, or you are sure to 
  2630. receive a protection violation. 
  2631.  
  2632. As a related topic, when you are initializing the PDRIVDATA field of the 
  2633. DEVOPENSTRUC structure, after allocating the memory, do not forget to set the 
  2634. cb field equal to the size of the buffer.  Otherwise, calling 
  2635. DevPostDeviceModes() to initialize this buffer will cause a protection 
  2636. violation. 
  2637.  
  2638. Select this to go to the next section 
  2639.  
  2640.  
  2641. ΓòÉΓòÉΓòÉ 3.2.5. Want Ads ΓòÉΓòÉΓòÉ
  2642.  
  2643. Below are the hot topics as of this issue's writing.  Feel free to write on any 
  2644. of these. 
  2645.  
  2646. Workplace Shell Programming (hot) - lately, I have noticed two things: 1) lots 
  2647. of people want to learn how to write new Workplace Shell classes and 2) no one 
  2648. who knows anything about it is telling the rest of us how to do it.  I'll even 
  2649. stoop down to accepting an article in ASCII format on this topic! :) 
  2650.  
  2651. Client/Server (hot) - using either named pipes (with or without a network) or 
  2652. sockets, client/server programming is all-the-rage these days.  On a related 
  2653. note, some people have also expressed an interest in learning about interfacing 
  2654. with the various protocol drivers (e.g.  NDIS, IPX/SPX, etc.).  Any articles in 
  2655. this area are most welcome. 
  2656.  
  2657. Anything on Rexx/2 (hot) - many people have requested more articles on Rexx/2. 
  2658. This issue sees the first article on this topic, but we can always use more. 
  2659. Writing "Enhanced Editor" macros in Rexx/2 and interfacing with the Workplace 
  2660. Shell from Rexx/2 are still open topics. 
  2661.  
  2662. Using Input Hooks (warm) - this is a complicated topic which was brought up 
  2663. frequently in the comp.os.os2.programmer.misc newsgroup. 
  2664.  
  2665. Hit testing (warm) - one reader noted that the Jigsaw sample in both the IBM 
  2666. and Borland toolkits (are they not the same?) perform there own correlation and 
  2667. wondered why?  Charles Petzold, in his OS/2 book "Programming the OS/2 
  2668. Presentation Manager" briefly describes correlation and hit-testing, but does 
  2669. not go into any detail nor does it describe the Gpi functions used for this 
  2670. purpose. 
  2671.  
  2672. Animation (warm) - a few readers expressed an interest in the various animation 
  2673. techniques that can be applied to PM applications.  The ultimate article, in my 
  2674. opinion, would be one that develops a sprite library a la the Commodore 64's 
  2675. (and Amiga's?) built-in routines, since this is probably the hardest component 
  2676. of any good animation sequence. 
  2677.  
  2678. Select this to go to the next section 
  2679.  
  2680.  
  2681. ΓòÉΓòÉΓòÉ 4. Reader's Choice Awards ΓòÉΓòÉΓòÉ
  2682.  
  2683. It is the end of our first calendar year in production and since we released 
  2684. our first issue in March of this year, we figured it made more sense to do this 
  2685. now instead of in February (otherwise, we will receive many questions asking 
  2686. why we didn't do this now).  So, without further ado, 'tis time for... 
  2687.  
  2688.                                 The First Annual
  2689.  
  2690. The editors want to know what you think the top three articles written during 
  2691. the past year are.  Please mail your responses to os2man@panix.com and indicate 
  2692. on what network you get EDM/2, if it is not the Internet (or the auto-mailer). 
  2693. For your convenience, the article titles have been included below. 
  2694.  
  2695. The "rules of the game" are... 
  2696.  
  2697. o Article series can be treated as either one entity or a group of separate 
  2698.   articles. 
  2699. o Each issue's columns should be treated as separate articles and can be voted 
  2700.   for separately. 
  2701. o If you wrote an article or a column, you may vote, but you may not vote for 
  2702.   something that you authored. 
  2703. o The three people that receive the most votes are placed in order of the 
  2704.   number of votes received.  Each person can place only once; thus, if John Doe 
  2705.   receives the most and second-most votes, while James Smith receives the 
  2706.   third-most, John is awarded first place and James is awarded second. 
  2707. o All votes must be email-dated before January 1, 1994. 
  2708.  
  2709. The winners of the Reader's Choice Awards not only get recognition in the next 
  2710. issue of the magazine, but they will also get a goodie (or two) which will not 
  2711. be revealed until the next issue. 
  2712.  
  2713. An index of the past seven issues: 
  2714.  
  2715. o A Review of C++ Compilers (issue 5) 
  2716. o Advance GPI:  Retained Segments and Transformations (issue 1) 
  2717. o Beginning Client/Server Programming:  Named Pipes (issue 2) 
  2718. o C++ Encapsulation of PM  (issue 4) 
  2719. o Customizing the Enhanced Editor (issue 6) 
  2720. o DOS Development Tools Under OS/2 (issue 3) 
  2721. o Development of a New Window Class - Part 1 (issue 4) 
  2722. o Development of a New Window Class - Part 2 (issue 5) 
  2723. o Getting Started with EMX/GCC (issue 1) 
  2724. o Introduction to PM - Gavin Baker (column) 
  2725. o Introduction to PM - Larry Salomon, Jr. (column) 
  2726. o OS/2 Installable File Systems - Part 1 (issue 3) 
  2727. o OS/2 Installable File Systems - Part 2 (issue 5) 
  2728. o OS/2 Installable File Systems - Part 3 (issue 7) 
  2729. o OS/2 Presentation Drivers in a Nutshell (issue 2) 
  2730. o Programming the Container Control - Part 1 (issue 3) 
  2731. o Programming the Container Control - Part 2 (issue 4) 
  2732. o Programming the Container Control - Part 3 (issue 5) 
  2733. o Questions and Answers (column) 
  2734. o Rexx-ercising Your Applications (issue 6) 
  2735. o Rexx-ercising Your Applications - Part 2 (issue 7) 
  2736. o Road Map for the Workplace Shell (issue 2) 
  2737. o Scratch Patch (column) 
  2738. o The Help Manager and Online Documentation (issue 5) 
  2739. o The Making of MineSweeper (issue 1) 
  2740. o The Unofficial Guide to the Palette Manager (issue 1) 
  2741. o Threads in PM Applications (issue 6) 
  2742. o Using OS/2 2.x bitmap files (issue 7) 
  2743. o Writing a C++ Thread Class (issue 6) 
  2744. o Writing a Direct Manipulation Spy (issue 7) 
  2745.  
  2746. Select this to go to the next section 
  2747.  
  2748.  
  2749. ΓòÉΓòÉΓòÉ 5. How do I get EDM/2? ΓòÉΓòÉΓòÉ
  2750.  
  2751. EDM/2 can be obtained in any of the following ways: 
  2752.  
  2753. On the Internet 
  2754.  
  2755. o All back issues are available via anonymous FTP from ftp.cdrom.com in the 
  2756.   /pub/os2/2_x/program/newsltr directory. 
  2757. o The EDM/2 mailing list.  Send an empty message to EDM2-info@knex.via.mind.org 
  2758.   to receive a file containing (among other things) instructions for 
  2759.   subscribing to EDM/2. 
  2760. o IBM's gopher server, which I have not the address of, in Almaden. 
  2761.  
  2762. On Compuserve 
  2763.  
  2764. All back issues are available in the OS/2 Developers Forum 2. (someone correct 
  2765. me if this is incorrect) 
  2766.  
  2767. Select this to go to the next section 
  2768.  
  2769.  
  2770. ΓòÉΓòÉΓòÉ 6. Contributors to this Issue ΓòÉΓòÉΓòÉ
  2771.  
  2772. Are You a Potential Author? 
  2773.  
  2774. As always, we are always looking for (new) authors.  If you have a topic about 
  2775. which you would like to write, send a brief description of the topic 
  2776. electronically to any of the editors, whose addresses are listed below, by the 
  2777. 15th of the month in which your article will appear.  This alerts us that you 
  2778. will be sending an article so that we can plan the issue layout accordingly. 
  2779. After you have done this, get the latest copy of the Article Submission 
  2780. Guidelines from ftp.cdrom.com in the /pub/os2/2_x/program/newsltr directory. 
  2781. (the file is artsub.zip)  The completed text of your article should be sent to 
  2782. us no later than the last day of the month; any articles received after that 
  2783. time may be pushed to the next issue. 
  2784.  
  2785. The editors can be reached at the following email addresses: 
  2786.  
  2787. o Larry Salomon - os2man@panix.com (Internet). 
  2788.  
  2789. The following people contributed to this issue in one form or another (in 
  2790. alphabetical order): 
  2791.  
  2792. o Andre Asselin 
  2793. o Larry Salomon, Jr. 
  2794. o Timur Tabi 
  2795. o Gordon Zeglinski 
  2796. o Network distributors 
  2797.  
  2798.  
  2799. ΓòÉΓòÉΓòÉ 6.1. Andre Asselin ΓòÉΓòÉΓòÉ
  2800.  
  2801. Andre Asselin recently graduated Cum Laude from Rensselaer Polytechnic 
  2802. Institute with a Bachelor of Science degree in Computer Science.  He has worked 
  2803. with OS/2 since version 1.3, and also has extensive experience with MS-DOS and 
  2804. Microsoft Windows.  He currently works in IBM's OS/2 TCP/IP Development group 
  2805. in Raleigh NC, where his responsibilities include the NFS client, a remote file 
  2806. system implemented as an IFS. 
  2807.  
  2808. Andre is also a member of Alpha Sigma Phi Fraternity, and enjoys hockey, 
  2809. soccer, and a good science fiction novel.  He can be reached via email at 
  2810. asselin@vnet.ibm.com or on CompuServe at 71075,133. 
  2811.  
  2812.  
  2813. ΓòÉΓòÉΓòÉ 6.2. Larry Salomon, Jr. ΓòÉΓòÉΓòÉ
  2814.  
  2815. Larry Salomon wrote his first Presentation Manager application for OS/2 version 
  2816. 1.1 in 1989.  Since that time, he has written numerous VIO and PM applications, 
  2817. including the Scramble applet included with OS/2 and the I-Brow/Magnify/Screen 
  2818. Capture trio included with the IBM Professional Developers Kit CD-ROM currently 
  2819. being distributed by IBM.  Currently, he works for International Masters 
  2820. Publishers in Stamford, Connecticut and resides in Bellerose, New York with his 
  2821. wife Lisa. 
  2822.  
  2823. Larry can be reached electronically via the Internet at os2man@panix.com. 
  2824.  
  2825.  
  2826. ΓòÉΓòÉΓòÉ 6.3. Timur Tabi ΓòÉΓòÉΓòÉ
  2827.  
  2828. Timur Tabi is a graduate student at the George Washington University in 
  2829. Washington, D.C, majoring in Computer Science/Hardware and Systems.  He is also 
  2830. the author of the The Ultimate OS/2 Game, a column in OS/2 Monthly  that covers 
  2831. advanced PM programming and computer game design.  After he graduates in May, 
  2832. he will join IBM's Multimedia development team in Boca Raton, Florida. 
  2833.  
  2834. Timur can be reached on Internet at timur@seas.gwu.edu and on Fidonet at 
  2835. 1:109/347. 
  2836.  
  2837.  
  2838. ΓòÉΓòÉΓòÉ 6.4. Gordon Zeglinski ΓòÉΓòÉΓòÉ
  2839.  
  2840. Gordon Zeglinski is a freelance programmer/consultant who received his Master's 
  2841. degree in Mechanical Engineering with a thesis on C++ sparse matrix objects. 
  2842. He has been programming in C++ for 6 years and also has a strong background in 
  2843. FORTRAN.  He started developing OS/2 applications with version 2.0 . 
  2844.  
  2845. His current projects include a client/server communications program that 
  2846. utilitizes OS/2's features which has entered beta testing.  Additionally, he is 
  2847. involved in the development of a "real-time" automated vehicle based on OS/2 
  2848. and using C++ in which he does device driver development and designs the 
  2849. applications that comprise the control logic and user interface. 
  2850.  
  2851. He can be reached via the Internet at zeglins@cc.umanitoba.ca. 
  2852.  
  2853.  
  2854. ΓòÉΓòÉΓòÉ 6.5. Network distributors ΓòÉΓòÉΓòÉ
  2855.  
  2856. These people are part of our distribution system to provide EDM/2 on networks 
  2857. other than the Internet.  Their help to provide access to this magazine for 
  2858. others is voluntary and we appreciate them a lot! 
  2859.  
  2860. o Gess Shankar (gess@knex.via.mind.org) - Internet 
  2861. o Paul Hethman (hethman@cs.utk.edu) - Compuserve 
  2862. o David Singer (singer@almaden.ibm.com) - IBM Internal 
  2863.  
  2864. If you would like to become a "network distributor", be sure to contact the 
  2865. editors so that we can give you the credit you deserve!