Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Really Nasty One..... (Callback's from a C DLL)

Status
Not open for further replies.

MikeLacey

MIS
Nov 9, 1998
13,212
GB
Ok, deep breath everyone... And it's long(ish) as well, sorry.

I'm writing (have pretty much written) an application that uses the Ghostscript DLL's - an implementation of a Postscript interpreter - to convert Postscript files to TIFF Image files.

The Ghostscript (GS) DLL functions are defined in VB like this:
[tt]
Private Declare Function gsdll_init Lib "gsdll32.dll" ( _
ByVal lpGsBack As Any, _
ByVal hwnd As Long, _
ByVal argc As Long, _
Argv As Long _
) As Integer

Private Declare Function gsdll_execute_begin Lib "gsdll32.dll" () As Integer

Private Declare Function gsdll_execute_cont Lib "gsdll32.dll" ( _
ByVal pscommand As String, _
ByVal commlen As Long _
) As Integer

Private Declare Function gsdll_execute_end Lib "gsdll32.dll" () As Integer

Private Declare Function gsdll_exit Lib "gsdll32.dll" () As Integer
[/tt]

The VB code calling these functions looks like this:
[tt]
[tab]rcode = gsdll_init(AddressOf Callback, f.hwnd, 8, llaArgV(0))
[tab]If (rcode = 0) Then
[tab][tab]rcode = gsdll_execute_begin()
[tab][tab]If (rcode = 0) Then
[tab][tab][tab]gsdll_execute_end
[tab][tab][tab]gsdll_exit
[tab][tab]Else
[tab][tab][tab]f.lstLog.AddItem "ERROR: rcode = " & rcode & " after gsdll_execute_begin"
[tab][tab][tab]rcode = gsdll_exit()
[tab][tab][tab]f.lstLog.AddItem "ERROR: rcode = " & rcode & " after gsdll_exit"
[tab][tab][tab]lsRetVal = vbNullString
[tab][tab][tab]Exit Function
[tab][tab]End If
[tab]Else
[tab][tab]f.lstLog.AddItem "ERROR: rcode = " & rcode & " after gsdll_init"
[tab][tab]lsRetVal = vbNullString
[tab][tab]Exit Function
[tab]End If
[/tt]

The good news is -- it works pretty well.
The bad news is -- when it doesn't it's pretty bad.....

You might have spotted the following line in the code above, it's where my problems start:
[tt]
gsdll_init(AddressOf Callback, f.hwnd, 8, llaArgV(0))
[/tt]

GS needs the address of a subroutine in my application (Callback), it uses this subroutine to inform me of its progress, any syntax problems it finds in the .PS file or problems in the parameters I've passed to gsdll_init. The parameters, inc the name of the file to be processed and the name of the output file, are passed in llaArgV(0).

The GS documentation specifies that the callback subroutine should be defined as:
[tt]
int gsdll_callback(int message, char *str, unsigned long count)
[/tt]

and gives some nice instructions on how to interpret the messages that will arrive. It also states that the Pascal calling convention is not used, which I don't know the details of but am aware that it has to do with the order in which arguments are placed on the stack.

All well and good, I'm sure it's just dandy when you're calling it from a C or C++ program; but I'm not.

I translate the C prototype above into the following VB definition:
[tt]
Private Function Callback(msgnum As Long, sptr As Long, slen As Long) As Long
[/tt]

This does not work. I get stack overflows, NT complaining that the data at so-and-so could not be 'read', general unhappiness.

If I define the function like this it works:
[tt]
Private Function Callback() As Long
[tab]Callback = 0
End Function
[/tt]

But I don't get access to the error messages produced by GS and I need them. Some error conditions require that the gsdll_exit() be called and others that it must *not* be called. This application will run unattended -- so it needs to cope on its own.

The people who write and maintain GS said "let us have a copy of the code when you get it working", which I suppose show's they're interested but doesn't help me.

I would appreciate any thoughts and comments you might have.

I am, of course, happy to post the results, source code and anything else people might find useful once the application is working.

Regards,
Mike
michael.j.lacey@ntlworld.com
Email welcome if you're in a hurry or something -- but post in tek-tips as well please, and I will post my reply here as well.
 
Hi Mike,
I am also developing application deeply involved with some nasty APIs and Callback mechanisms. Anyways, I have 4 comments about the code you posted, 1st The AddressOf operator send a memory address to your API so you declaration should use AS Long instead of AS Any….
Private Declare Function gsdll_init Lib "gsdll32.dll" ( _
ByVal lpGsBack As Long, _
ByVal hwnd As Long, _
ByVal argc As Long, _
Argv As Long _
) As Integer

Second, according to Microsoft documentation, the callback procedure must be located in standard module. So if you don’t make the call to your callback function from inside this standard module, make sure that you declare you callback as Public not Private.

Thread, about your translation of the C syntax
int gsdll_callback(int message, char *str, unsigned long count).

As far as I concern, should be

Public Function Callback(ByVal msgnum As Integer, ByRef sptr As Long, ByVal slen As Long) As Integer
Finally, you don’t need to worry about the scope operator when you define your DLL functions like
Private Declare Function gsdll_init Lib "gsdll32.dll" ( _
ByVal lpGsBack As Any, _
ByVal hwnd As Long, _
ByVal argc As Long, _
Argv As Long _
) As Integer

Private Declare Function gsdll_execute_begin Lib "gsdll32.dll" () As Integer

Private Declare Function gsdll_execute_cont Lib "gsdll32.dll" ( _
ByVal pscommand As String, _
ByVal commlen As Long _
) As Integer

Private Declare Function gsdll_execute_end Lib "gsdll32.dll" () As Integer

Private Declare Function gsdll_exit Lib "gsdll32.dll" () As Integer
You can just remove the word private from all of that, unless you trust what you doing and have a clear scope vision in your mind, If you want to declare without scope copy all you declarations to a separate module ( don’t define any functions or procedures, just copy all your declarations inside this module) Visual basic will automatically call this module in the start up
I hope this will help, and I will really appreciate it if you tell me if it works or not.
Thanks,
Walid Magd
Engwam@Hotmail.com
 
Thanks Wallid,


You say:

int gsdll_callback(int message, char *str, unsigned long count).

As far as I concern, should be

Public Function Callback(ByVal msgnum As Integer, ByRef sptr As Long, ByVal slen As Long) As Integer

Ok.... <doubtful, but interested> why all the ByVal's and ByRef's then?

(thanks by the way, I'll be trying all of this stuff tomorrow) Mike
michael.j.lacey@ntlworld.com
Email welcome if you're in a hurry or something -- but post in tek-tips as well please, and I will post my reply here as well.
 
I am sorry, I don't understand...
You said..

Ok.... <doubtful, but interested> why all the ByVal's and ByRef's then?
// The scope operator Public make your function visible for the entire project in case you will call it from out side the module it has been defined in.
ByVal's and ByRef's define the way you will pass your parameters, remember that &quot;char *str&quot; is a pointer in the C syntax. Anyways, I didn't get your point in the 2nd post
If I am wrong or you see something invisible to me, Please correct me.
Thanks Walid Magd
Engwam@Hotmail.com
 
Wallid,

My apologies, I didn't express myself very well.

The main point of my second post was to thank you for responding to my long and technical question.

I also program in C, so I'm somewhat familiar with pointers. I use ByRef and ByVal in VB as well -- my question was about their use in this particular context.

What difference will they make when the function will be called from outside my program? I'm not disagreeing with you -- I don't know enough about this yet to have a proper opinion -- I just don't understand what they will do here.

Again. Thanks for your thoughtful reply, it's much appreciated.

Regards,
Mike
michael.j.lacey@ntlworld.com
Email welcome if you're in a hurry or something -- but post in tek-tips as well please, and I will post my reply here as well.
 
The callback problem is the calling convention, the mechanism I have to use to supply the address of the callback function is the [tt]AddressOf[/tt] operator.

From the VB documentation of [tt]AddressOf[/tt]:

&quot;... your prototype must use the __stdcall calling convention. The default calling convention (__cdecl) will not work with [tt]AddressOf[/tt].&quot;

From an email from the Ghostcript guys in Australia:

&quot;The callback function uses the C calling convention, not WINAPI/__stdcall. If you get this wrong, you will get stack overflow or corruption&quot;

Yep -- that's what I'm getting....

So. Ho hum - I suppose, unless they'd like to write me a version of gsdll_init that uses the __stdcall convention....

This leaves me in the unhappy position of not being able to get error messages from GS. I do not want to get into writing a &quot;wrapper&quot; DLL in C to call gssdll_init that would provide a callback function that uses __stdcall, so I'm not sure where to go from here. Any thoughts?

I'm happy to share the example application source code with anyone interested, it works but does not cope well with PS syntax errors.

Walid -- thanks again for your contribution.
Mike
michael.j.lacey@ntlworld.com
Email welcome if you're in a hurry or something -- but post in tek-tips as well please, and I will post my reply here as well.
 
. I do not want to get into writing a &quot;wrapper&quot; DLL in C to call gssdll_init that would provide a callback function that uses __stdcall, so I'm not sure where to go from here. Any thoughts?
///////////////////////////
I don't know why you don't do that, it looks like the most practical solution you have, also I don't know what do you mean by DLL. do you mean a standard or COM-Based in process DLL server. If I were you, I will use ATL to make COM-Based in process DLL server, the code will be minimum. you will just &quot;Wrapper&quot; all you functions call to methods. Make sure that the whatever callback procedure you will use will call one of your methods, so when you instanciate your class in VB you can override it to do and return whatever.
Good luck
Walid Magd
Engwam@Hotmail.com
 
Walid,

I agree that writing a wrapper DLL (yep, I meant an in-process server) sounds like a practical way forward -- however:

1. I'm a UNIX C person, not Win32 -- never written a DLL in my life so I would have to learn how.
:-( No thanks.

2. I'm the only C programmer at my location so I would be creating a support problem for future maintenance -- in other words, I would have to maintain it myself. For ever.
:-( No thanks.

I plan to release a VB module to the Ghostcript community and, hopefully, this will generate some interest and encourage someone *else* (preferably in the GS Community so that it would have a chance of being widely used and therefore supported) to write a wrapper DLL... I'm not holding my breath though. (unless you're interested...?)

For the moment -- I don't know. The program as it stands detects that there *is* an error but gives no information about what it is. That *may* be sufficient for this application....

If it's not, I'm toying with the idea of using an application supplied with GS itself to parse the PS and report the error. That would involve calling, controlling and maybe exiting another application -- messy. Mike
michael.j.lacey@ntlworld.com
Email welcome if you're in a hurry or something -- but post in tek-tips as well please, and I will post my reply here as well.
 
Well, I don't realy know if I am interested in this GS or not, I mean I don't know what it is suppose to do or what value it suppose to provide you, can you kindly give me a short story about it
And/or some web links for more info
Walid Magd
Engwam@Hotmail.com
 
Ok....

is their website, you can get the s/w there if you're interested, complete with source code and quite a bit of documentation.

Ghostscript is an implementation of an interpreter for the Postscript language.

We use it in a fairly boring way I guess. Difficult stuff though, for me at least.

A system produces an invoice (or something) in Postcript, it needs to be faxed to a customer or supplier so it has some addressing information in there are well.

The file is ftp'd to the workstation with my VB program which picks it up and converts it to a TIFF image, suitable for faxing.

The TIFF file is ftp'd to the fax server -- and away it goes.

We process about 3000 faxes a day, 1 per minute or so. The VB program is, in effect, a real-time system that has to react to events in the outside world, files turning up, networks going down and coming back up, syntax errors in Postcript files, bad addressing information -- all that.

The old version uses something called Image Alchemy to do the conversion. It takes up to 40 seconds to do a conversion and there is often &quot;quite a backlog&quot; of faxes wating to go to the fax server.

The new system converts PS files to TIFF in around 0.6 of a second -- so I'm hoping to get rid of the backlogs.

It must run unattended, 24 hours a day, report errors and generally behave and not leak memory all over the place.

Simple in concept but, as I said, difficult stuff when you get down to it.
Mike
michael.j.lacey@ntlworld.com
Email welcome if you're in a hurry or something -- but post in tek-tips as well please, and I will post my reply here as well.
 
Mike,

I also had the same point of view when investigating the Ghostscript dll from VB - that is, to put my code in the public domain (assuming I am successful) since there are no examples at all for VB!

I am simply trying to convert EPS files to BMP's or to anything that can be displayed in a VB image control.
I am not seeing what arguments need to be passed to the gsdll_init function. Can you help with that? Also, is Dan Appleman's AddressFor... function required, or can that be done with VB inline functions?

I tried the COMGS.DLL but was unable to get it to work with GS6.5. I am trying this with both GS6.5 and with the newer GS API in 7.0.

TIA,
Greg gfox@dma2.com

 
I was using GS5.something & COMGS.DLL did not work.

GS 5.50 & COMGS.DLL worked fine, first time, so I guess it is version dependant

Nick
nick.hall@altasystems.co.uk






 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top