How to call external Windows library

Discussion in 'Rebol' started by swhite, Jan 29, 2011.

  1. swhite

    swhite Member

    I am trying to figure out how to call an external Windows library.
    I find the REBOL documentation (REBOL External Library Interface)
    not perfectly clear, and the library is in C which I don't understand,
    and I don't have the source for the library so I can't look at it
    anyway, and the documentation for the library is in C lingo. So I
    am at a disadvantage. Nevertheless, I am plodding ahead doing what
    seems logical based on the information I have, and of course it is
    not working. I am wondering if anyone can give me any insight.

    The library is from the US Postal Service, and encodes the zip code
    and a tracking number in to a 65-character string which then is
    printed in a bar-code font to produce the new Intelligent Mail
    bar-code.

    Here is the test script. I am thinking there is just a syntax error
    somewhere; I am not expecting anyone to know how to use this
    specific Windows library.

    REBOL [
    ]

    print "script has started"

    ;; [---------------------------------------------------------------------------]
    ;; [ Parameters to library routine, in required order. ]
    ;; [ ]
    ;; [ The calling code, in C, would be ]
    ;; [ RetCode = USPS4CB(TrackString,RouteString,BarString); ]
    ;; [ We have to make the REBOL code accomplish this. ]
    ;; [---------------------------------------------------------------------------]

    TrackString: "00700901032403000000" ;; tracking number input
    RouteString: "55431308099" ;; zip code input
    BarString: make string! 66 ;; characters returned by library call

    IMBLIB-FILE: %/c/servise/uspsimb/usps4cb.dll ;; name of library file

    ;; Bring library into memory and create a name with which to refer to it.
    IMBLIB: load/library IMBLIB-FILE

    print ["IMBLIB type is " type? IMBLIB]

    ;; Define a REBOL procedure which, when called, will call the "USPS4CB"
    ;; procedure in the library.
    GEN-CODESTRING: make routine! [
    "This is the procedure to encode the zip code and tracking data"
    TRACKING-NUMBER [string!] "First 20 digits"
    ROUTING-NUMBER [string!] "Zip plus 4 plus 2"
    CODING-STRING [string!] "65 characters of ADTF"
    return: [integer!]
    ]
    IMBLIB "USPS4CB"

    print ["GEN-CODESTRING type is " type? GEN-CODESTRING]

    print "Calling USPS4CB"
    RetCode: GEN-CODESTRING TrackString RouteString Barstring
    print "Routine is done"

    print BarString

    halt

    And here is what happens when I run it with REBOL/Command 2.5:

    REBOL/Command 2.5.6.3.1
    Copyright 1997-2003 REBOL Technologies
    REBOL is a Trademark of REBOL Technologies
    All rights reserved.

    Component: "REBOL Mezzanine Extensions" 1.1.2.1 (29-Nov-2002/19:29:09)
    Component: "Windows Registry Access" 1.1.2.4 (11-Dec-2002/0:36:56)
    Component: "REBOL Internet Protocols" 1.59.2.15 (14-Feb-2003/1:45:14)
    Finger protocol loaded
    Whois protocol loaded
    Daytime protocol loaded
    SMTP protocol loaded
    POP protocol loaded
    IMAP protocol loaded
    HTTP protocol loaded
    FTP protocol loaded
    NNTP protocol loaded
    Component: "ODBC Access" 1.2.2.2 (24-Mar-2002/20:13:57)
    Component: "Dynamic Library Access" 1.4.2.1 (24-Mar-2002/20:13:53)
    Component: "Oracle Access" 1.2.2.2 (24-Mar-2002/20:13:57)
    Component: "MySQL Access" 1.1.2.2 (24-Mar-2002/20:13:57)
    Component: "Command Shell Access" 1.5.2.5 (11-Dec-2002/0:37:05)
    Component: "Graphics" 1.1.2.5 (23-Jul-2003/21:32:34)
    Component: "Encryption" 1.3.2.2 (24-Mar-2002/20:13:52)
    Component: "Big Numbers" 1.2.2.2 (24-Mar-2002/20:13:52)
    Component: "DH/DSA Encryption" 1.2.2.2 (24-Mar-2002/20:13:52)
    Component: "RSA Encryption" 1.3.2.2 (24-Mar-2002/20:13:58)
    Component: "FastCGI" 1.2.2.3 (24-Nov-2002/17:05:54)
    Component: "System Port" 1.1.2.5 (30-Nov-2002/17:24:03)
    Component: "Encryption Level 2" 1.1.2.3 (24-Jan-2002/19:54:44)
    Component: "Secure Socket Layer" 1.3.2.2 (24-Mar-2002/20:13:58)
    Component: "Licensing" 1.8.2.6 (30-May-2003/7:32:41)
    Licensed to: Amy Cheney (commercial) <swhite@ci.bloomington.mn.us>
    License ID: 5-00341-1
    Script: "Untitled" (none)
    script has started
    IMBLIB type is library
    ** Script Error: Expected one of: string! - not: unset
    ** Near: type? GEN-CODESTRING
    >>
  2. Graham

    Graham Developer Staff Member

    Need more information on what the library call is and what is expected in terms of C datatypes.
  3. swhite

    swhite Member

    I was hoping I had just made a syntax error. I don't want to make anyone beat their heads on this for me. But if you can spot the problem, here is what might be relevant C code from a program, provided with the library, to call the library with some test data and report the results.

    This seems to define the input and output data items, plus the integer return code that the library uses to reports success or failure.

    char TrackString[21]; /* Input parameter Track String + 1 null*/
    char RouteString[12]; /* Input parameter Route String + 1 null*/
    char BarString[66]; /* Output parameter Bar String + 1 null */

    int RetCode; /* Return code from the usps4cb encoder */

    This seems to be the line of code that calls the library.

    /****************************/
    /* Call the usps4cb encoder */
    /****************************/

    RetCode = USPS4CB(TrackString,RouteString,BarString);

    I see that the input data items (TrackString and RouteString) have a trailing null to delimit them. I don't know how to, or if I can, or if I should, provide that in the REBOL code. I got the impression from the REBOL documentation that those mappings of REBOL data to C data were part of the "make routine!" operation.

    Thank you.
  4. Graham

    Graham Developer Staff Member

    My experience is that for a called library to return a string, you have to create a structure to hold that string ... and pass a pointer instead.
  5. MaxV

    MaxV Member

  6. swhite

    swhite Member

    Thank you.

    I might have been using the wrong version of REBOL. I fixed that issue, then copied the most basic of Nick's examples to my own computer, and ran it, and it worked. Then I visually compared his code to mine, and it appears that I have no syntax errors. I tried it again and it still did not work. Since the only REBOL coding I have to do is to define the parameters of the library function, and then call the library function with those parameters, the error must be there somewhere.

    So I will have to beat my head on it after all. I'll go over the documentation again, try a few things, and see what I can do.

    This shows an interesting feature of REBOL. Based on the output of my test script, the problem is in the "make routine!" function. Excluding comments, that's SEVEN lines of code. One would think a guy should be able to find a problem in SEVEN lines of code. Makes a guy miss the days of assembler language.
  7. Graham

    Graham Developer Staff Member

    Also, it's not uncommon for interface library documentation to be incorrect. e.g it may say that a string is required when it should be a pointer instead.
  8. MaxV

    MaxV Member

  9. swhite

    swhite Member

    Thank you. This is all a bit too "C-ish" for me, but it got me thinking. I commented out the parameter declaration, giving:

    GEN-CODESTRING: make routine! [
    return: [integer!]
    ;; TRACKING-NUMBER [string!]
    ;; ROUTING-NUMBER [string!]
    ;; CODING-STRING [string!]
    ]
    IMBLIB "USPS4CB"

    and the script runs to completion, but of course without correct results. If I uncomment one of the parameter definitions, I get the same error. So, the problem now has been narrowed down to THREE lines of code.

    I have solved the problem of getting the barcode by a different method, so I am not under the gun in any way to solve this problem, but it is becoming interesting, so I might pursue it at my leisure.
  10. notchent

    notchent Member

    Hi swite,

    This works:

    Code:
    rebol []
    
    TrackString: "00700901032403000000"
    RouteString: "55431308099"
    empty-string: (make string! 65)
    
    IMBLIB: load/library %usps4cb.dll
    GEN-CODESTRING: make routine! [
        TRACKING-NUMBER [string!]
        ROUTING-NUMBER [string!] 
        CODING-STRING [string!]
        return: [integer!]
    ] IMBLIB "USPS4CB"
    
    print mold first :GEN-CODESTRING
    
    RetCode: GEN-CODESTRING TrackString RouteString empty-string
    
    print mold first :GEN-CODESTRING
    
    print rejoin ["^/^/Your string is:  " first second first :GEN-CODESTRING]
    
    halt
    
    At first, I thought the same thing as Graham - usually when a value is modified within a DLL function (i.e., not returned by the function), a structure is used to hold the changed value. But here the function clearly requires only string values (no structures). The answer is in the "Using C Pointers" section of http://www.rebol.com/docs/library.html:

    So, "first second first :GEN-CODESTRING" holds the changed string :)
  11. Graham

    Graham Developer Staff Member

  12. notchent

    notchent Member

    I prefer this edited version. It allows you to find the DLL on your hard drive, and then enter tracking and routing numbers (with default #'s provided). 7 lines :)

    Code:
    GEN-CODESTRING: make routine! [
        t [string!]  r[string!]  c [string!]  return: [integer!]
    ]  load/library request-file/only/file %usps4cb.dll "USPS4CB"
    
    t: request-text/title/default "Tracking #:" "00700901032403000000"
    r: request-text/title/default "Routing #:" "55431308099"
    GEN-CODESTRING t r (make string! 65)
    alert first second first :GEN-CODESTRING
    
    ...ooh, and I can't help myself ... a 4 line version:

    Code:
    G: make routine! [t [string!] r [string!] c [string!] return: [integer!]
    ] load/library %usps4cb.dll "USPS4CB"
    G "00700901032403000000" "55431308099" (make string! 65)
    alert first second first :G
    
    That was fun :)
  13. notchent

    notchent Member

    Graham, have you noticed that the last post in each thread is always scrunched horizontally (maybe just in my browser). Is there a remedy for that?
  14. Graham

    Graham Developer Staff Member

    Nick, nope ..can't say that I have. What are you using?
  15. notchent

    notchent Member

    I just checked with Firefox, IE, and Opera. Take a look at http://synapse-ehr.com/forums/showthread.php?139-Rebol-CMS, and you'll see that the last entry is clearly thinner than the rest. If you add a new entry, that existing entry will expand to the full width. Sometimes that issue makes code wrap unintentionally. Not a big problem, but a consistent little frustration.
  16. swhite

    swhite Member

    Thank you! I can't believe this; this is so bad. I looked at your code for loading the library and defining the routine, and it looked the SAME as mine. I was SURE that the problem was in the defining of the parameters. But it wasn't. The script was returning an error on the DEBUGGING statement I put in AFTER the routine definition in order to see if the routine definition was working in the first place. I thought that the code to define the routine was wrong because the script never got to the "print" statement. But it DID get to the print statement. It got there, and THEN it crashed. I just now ran my original code without the print statement, and it worked. (Actually, it did not work CORRECTLY, but it ran to completion, and I finally got the correct results using the code you provided.)

    It might be time to retire.

Share This Page