Capture data from another apllications print job

evanscolin
02-12-2008, 08:01 AM
Hi, is it possible to capture data from another programs print, I want to strip out the information sent to a printer, basically its a label print that outputs the surname, forename, address etc. I want to capture the information to use in my add on program.

IF I print to a generic / text print and save to a file the information is presented in a format that I can use, what I want to be able to do is put my own generic / text printer in there which doesn't ask for a filename, just either kicks off another program with the information or simply adds the information to a database that my other program could use.

Its the interface acting as a printer thats is causing me problems as I wouldn't know where to start.

Could anyone help, thanks

loquin
02-12-2008, 12:50 PM
Is there no underlying database you could access?

What is the other program?

evanscolin
02-13-2008, 07:55 AM
Hi Lou

Thanks for the reply, the third party program simply issues the label with the patients name, address etc held on it, all I want to do is capture the label and then do something else for the person before my program then prints out a label. It need to capture the label data as its generated to the printer,

I thought there may be a way of creating a generic text printer that captures the data but instead of printing to a file it simply puts the information in to either a text file or a database, all I'd need to do is strip the data into the various fields then put a line of data, each field seperated by a comma into a list within another text file or database

do you think its possible to create a print capture program, presumably I'd install the program as a printer in windows and when the label is printed to it it would do the business

Cheers.

loquin
02-13-2008, 10:28 AM
I'm sure it's possible, but, I'm guessing it would take a lot of effort in C to get a custom print driver working (Or, buying an expensive print driver toolkit...).

Now, one approach might be to set up a generic text printer, with output going to a defined file name and path. Your existing app would print to the text driver.

Add a VB folder monitoring app which is checking for the new file in the folder you've specified. (Although you could poll the folder every second or so, I believe that there's an API approach to folder monitoring where an event is raised. (Search the site for folder monitoring.) When the monitoring app detects a new print file, it reads it, extracts the data, and then IT prints the label and saves the data. Finally, it would kill the print file.

Although this approach would add a second or so to the label print, it should capture the data without thousands of hours in custom driver development, (or lots of $$$)

evanscolin
02-19-2008, 10:04 AM
Hi Lou, using the process you mentioned would it be possible for the generic printer to be setup to print to a file automatically without user intervention, i.e. bypass the need to enter a file name.

I've had a look at the text file printer but can't seem to find anyway of printing to a specific filename, without this I don't think this way of working would be useable

Cheers for the input, you mentioned C are there any forums that you are aware of that may have something along these lines...

loquin
02-20-2008, 11:40 AM
Yes.

Add a new printer. Select "Create a new Port", then enter the full file pathname into the port name popup window. In the next step, select the generic text printer. Ref the two pics below.

As far as coding a custom print driver - I looked into it a couple of years ago. There are some libraries and "toolkits" available, but they're high $ items. I never found a forum specifically for this. If you hit the C forum at ProgrammersTalk.Com, they may be able to provide some more info.

evanscolin
02-20-2008, 03:39 PM
Hi Lou, many thanks again for your solution, the next bit may be just as hard doing ti this way, but I assume there is a way to identify if the file exists from a background job, extract the information and then delete the file ready for the next file to be created.

Presumably this would be just a check to see if the file exists every x seconds then process it, is there a correct way to do a background job to action this, your input is greatly appreciated

Thanks again

loquin
02-20-2008, 11:12 PM
You can poll the folder, as you mention, but take a look at this:

http://www.desaware.com/tech/filemonitoring.aspx

evanscolin
02-21-2008, 12:58 PM
Hi Lou, dare I ask, you could show me in a few lines how I'd use this code to check if the file exists or has been changed, presumably in the loop thats shown I'd open the file read in each line then process the data, which I'd want to save to an access database. The concept of having this program running in the background is totally new to me are there any steps I need to take to create a program thats constantly running and updating a database, won't it have processing elements that I'd need to consider, lots of questions but grateful of any input you can offer, especially if I can learn via some source code :chuckle:

many thanks once again

loquin
02-22-2008, 12:19 PM
take a look at the examples code: FindFirstChangeNotification

first you set the FindFirst change notification hook, and then, you call WaitForSingleObject Your app then pauses until something changes in the folder that you referenced when you set the notification hook.

Once anything changes there, you'll need to process the change, and then immediately call the FindNEXT change notification hook, and the waitforSingleObject call, which again pauses your app until a change ocurrs.

This portion of the code (findnext) in your code should be placed in an endless loop. a Do While True = False loop will do that for you.

Since you won't be ending the loop, but you want to remove the hook when the app ends, place the FindCloseChangeNotification hook removal code in the form unload event. Obviously, you'll need to make the ret variable a form level variable for this.

Your app should be set up so as to not show on the task bar, and it should have a shortcut in the Startup folder. And, when running it, have the main form be minimized.

Now - the processing.

Since you'll set up the text printer to create a file, I would cause that file to be written in a sub-folder on the local disk drive, and not the root folder itself.

If the user sees an empty file, they may delete it. So, that would cause a change event. So, in your event processing sub,

First, you'll need to see if the folder is still there. If not, the user has deleted it. Recreate the folder and exit the sub. (which may puzzle them, so you should pop up an error message, saying that the "folder is needed for label printing - don't delete it or add any files to it!"

Next, check to see if the file name you're expecting is present. If it is (the normal thing that should happen when you get the file, open it, process it, close it, kill it, and exit the sub.

If the file ISN'T there, then the user has added something else. Or, maybe they've changed the security or attributes for the folder. First, check the folder attributes and security settings. If they've changed, change them back, and issue a message to the user, telling them that they're naughty. And exit.

If the folder security and attributes haven't been changed, then there must be another file in the folder. Do a directory scan, and every file except the one you want should be moved to the root folder, and the user chastized...

(notice that the BULK of this code is checking for events that shouldn't happen? ... that's normal!)

Finally, the only thing left is that there IS a new file in the folder, and it is the file you expect. You'll process it by

opening the file
reading the file into memory (I like to load it all at once, into a single string, and then split it into an array of lines - ref the Fast text file I/O post in the *** Close the source file parse the text from the file, grabbing the information you need, and write it to your Access database. (Connect, Construct a SQL INSERT statement, connection.Execute strSQL, Close Connection, Release Connection) Kick off your OWN label print routine, to the real label printer.
Now, your processing sub is done. Kill the source file and exit.
(Back within the original endless loop, your app will then hook the change event, and wait for the next event, within the endless loop)

Pseudo Code
Set Folder Hook
WaitForFirstEvent
ProcessTargetFolder

Do While True = False
Set Folder Hook
Wait For NextEvent
ProcessTargetFolder
Loop

End Sub

Sub Form_Unload
Unhook Folder Notification
End Sub

Sub ProcessTargetFolder
Error Checks & Wrist Slapping messages (exiting sub when done)
Read the File
Parse the File
Save the Data
Print The Actual Label
End Sub
Note that you should also probably put in the code to make sure the app only loads once. App.PrevInstance stuff.

evanscolin
02-24-2008, 01:20 PM
Hi Lou, what can I say, thank you very much, I'll study everything you've told me and hopefully I'll manage from now on.

Once again, thanks for all your help, best regards, Colin

evanscolin
02-27-2008, 11:18 AM
Hi Lou, sorry to come back so soon but in your example you mention the Do WHile True = False

Is this valid as I've tried the following but it doesn't seme to process

Private Sub Form_Load()
Dim ret As Long
ret = FindFirstChangeNotification("C:\ceCHK", &HFFFFFFFF, FILE_NOTIFY_CHANGE_ALL)
WaitForSingleObject ret, &HFFFFFFFF
DProcess

Do While True = False
ret = FindFirstChangeNotification("C:\ceChk", &HFFFFFFFF, FILE_NOTIFY_CHANGE_ALL)
FindNextChangeNotification ret
WaitForSingleObject ret, &HFFFFFFFF
DProcess
Loop
End Sub

Private Sub DProcess()
MsgBox "this is where things get processed when there's a change"
End Sub

Obviously I'm doing somthing wrong and I hold my hands up as being a total novice :-) the first DProcess works fine but nothing in the loop

evanscolin
02-27-2008, 12:09 PM
Hi Lou

Just been trying a few things out, would this be acceptable as the way to initiate the loop.

If I have an Exit button 'cmdClose' on the form will this work rather than Do While True = False

Do While cmdClose.Value = False

Hi Lou, further to the above, I've tried this

Private Sub Command1_Click()
'do whatever is needed before calling form unload procedure
Ret = FindFirstChangeNotification("C:\ceweb\dispensechk", &HFFFFFFFF, FILE_NOTIFY_CHANGE_ALL)
FindCloseChangeNotification Ret
Unload Me
End Sub

Private Sub Form_Load()
Dim Ret As Long
'process first change
Ret = FindFirstChangeNotification("C:\ceweb\dispensechk", &HFFFFFFFF, FILE_NOTIFY_CHANGE_ALL)
WaitForSingleObject Ret, &HFFFFFFFF
DProcess
'now process all the others
Do While cmdClose.Value = False
FindNextChangeNotification Ret
WaitForSingleObject Ret, &HFFFFFFFF
DProcess
Loop

End Sub
Private Sub DProcess()
MsgBox "Event Triggered from change in directory"
End Sub

It appears to work okay but if I copy and paste a file into the folder I get 2 msgbox "Event Triggered from change in directory" dialogs but if delete a file in the folder I only get 1 msgbox "Event Triggered from change in directory" dialog

Any idea's

loquin
02-27-2008, 02:11 PM
First, place the FindCloseChangeNotification Ret IN the unload event. What if someone closes the app by clicking the X button? The unload event is ALWAYS called (unless you're using the END statement somewhere (*&*X-#&%*!!).

Second, make RET a form level variable. (DIM it at the top of the code module.) If you are going to set it inside one sub, and you need to use it inside another, it will need to be at a higher scope level... (It is a handle to the change notification event)

Third - DON'T call the FindFirstChangeNotification function again in the close button code. This will leave a change notification event sitting in memory, as you are currently abandoning the value of the existing change notification handle that you obtained in the form load event. If you place the scope of RET higher, all the subs can access the one change notification event. Which is what you want.

I would probably also add a form level boolean flag value (blExit) and refer to IT from your loop. In the cmdExit code, set the blExit value to True.

(BTW. The above pseudo code should have been Do...Loop until True=False)

I'm curious. Does your app SEE the button click event to close the app? Since it's waiting at the WaitForSingleObject call?

As far as the 2 events from copy/paste - It could be several different events, as you are monitoring all changes in the folder, as you've specified the &HFFFFFFFF Flag. I believe what you're seeing is one event when the file is created, and one event when the folder last write time stamp is updated

You could limit this by only monitoring for a change in file name. (I believe by using const FILE_NOTIFY_CHANGE_FILE_NAME, or possibly Const FILE_NOTIFY_CHANGE_LAST_WRITE) If the disk drive is heavily cached, you may not see the last_write event until the cache is flushed.

Also, note that since you'll be deleting the print file when you're done processing it, you should ignore those events which result in the folder being empty)

evanscolin
02-28-2008, 09:21 AM
Hi Lou, I've made the changes you suggested and it seems to be working fine, just a couple more questions if you don't mind.

1. You mention the &HFFFFFFFF Flag is there somewhere where I can find out the various flags,

2. Now that I can get to open the file, following your explantion in the FIle I/O tutorial, I can't seem to find what delimeter I should use to seperate the values into the array

I've tried several;

MyArray = Split(MyString, vbCrLf)

MsgBox MyArray(0)
MsgBox MyArray(1)

in this example as well as vbTab, vbCRLF and vbNewLine I get everything in the MyArray(0) but then a subscript out of range in MyArray(1)

I've attached a sample file to show the delimeter character, which I did assume to be a Tab or linefeed, I'm sure you'll be able to identify what I should be using, hopefully

Once again many thanks for all your help in this matter, greatly appreciated.


Edit.....
Whoops, trial and error, tried it with just vbLF and it works, is there quick way to know how big the array is?

Thanks again

evanscolin
02-28-2008, 10:05 AM
Sorry Lou, again through trial and error I sorted it, Ubound worked perfectly, cheers, hopefully I'm getting better and will try to think before I post, thanks again

loquin
03-02-2008, 04:48 PM
1: In the Allapi source code example for FindFirstChangeNotification that I linked to above, there are a list of the flags as constants in the code. If you want to use multiple flags, OR them together.

2: After loading the entire file into a string, temporarily loop through the string, one character at a time, printing the character and the Ascii code.
' temporary code to investigate the characters in the string...
Dim N as Long, S as string
For N = 1 to len(strFile)
S = Mid$(strFile, N, 1)
Printer.Print N, S, Asc(S)
Next N
Printer.EndDocYou'll have a list of all the characters in the file, with their ascii codes to use in the split statement.

evanscolin
03-04-2008, 06:01 AM
Hi Lou, back again I'm afraid

Things have been going very well and I'm nearly there (I think) only one thing bugging me and its the number of times it does the loop when a new file (label) is created in the folder,

Ret = FindFirstChangeNotification("C:\ceweb\dispensechk\label", &HFFFFFFFF, FILE_NOTIFY_CHANGE_LAST_WRITE)

WaitForSingleObject Ret, &HFFFFFFFF

Do Until True = False
FindNextChangeNotification Ret
WaitForSingleObject Ret, &HFFFFFFFF
DProcess
Loop

THe code above calls DProcess twice and then goes back to the wait status, and then when another label is printed it does the loop twice again.

I changed to FILE_NOTIFY_CHANGE_LAST_WRITE and this always does the loop twice whereas FILE_NOTIFY_CHANGE_ALL sometimes did it once and then sometimes did it twice.

I'm at a loss of how the &HFFFFFFFF makes an effect and also I haven't look at what will happen when I delete the file after processing, how do I not do anything in this case.

Thanks again for everything so far, hopefully you can point me in the right direction again but please make it simple, brain dead at the moment :-)

Best regards, Colin

loquin
03-05-2008, 11:52 PM
Ret = FindFirstChangeNotification("C:\ceweb\dispensechk\label", &HFFFFFFFF, FILE_NOTIFY_CHANGE_LAST_WRITE)


I'm sorry - I told you wrong earlier. The &HFFFFFFFF flag is a true/false value which specifies whether a change in the entire directory tree from the names folder down will trigger a change, or just the named folder. &HFFFFFFFF is equal to -1, so use a 0 (I believe) to set this property to false, and only monitor the named folder.

Now. per allapiValue Meaning
FILE_NOTIFY_CHANGE_FILE_NAME Any file name change in the watched directory or subtree causes a change notification wait operation to return. Changes include renaming, creating, or deleting a file name.So, what I believe may be happening is that the windows print spooler is first creating the file with a temporary name, and then renaming it when printing is complete. Thus, triggering two, separate events in your code. If you want to verify this, temporarily call a directory print routine, which will dump the named folder file contents to a text file. Then, monitor the folder with your app while printing to the text file printer.

Something along these lines: Private Sub DumpDir
Dim strPath as string
Dim strFile as String
Dim ifn as integer

ifn=freefile

strPath = "C:\ceweb\dispensechk\label\*.*"

' open the log file
Open "C:\LogFile.Txt" for append as #ifn
print #ifn, timer

strFile = Dir(strpath)

Do while lenb(strFile)>0
Print #ifn, strFile
strFile = Dir
Loop

print #ifn, vbnewline & vbnewline
Close ifn
End Sub

For the test, call this sub instead of DProcess.

Then, open the log file, and see what it shows...

A question... why aren't you calling both before the DO statement, and inside the loop?

i.e.Ret = FindFirstChangeNotification("C:\ceweb\dispensechk\label", &HFFFFFFFF, FILE_NOTIFY_CHANGE_LAST_WRITE)

WaitForSingleObject Ret, &HFFFFFFFF
DProcess

Do Until True = False
FindNextChangeNotification Ret
WaitForSingleObject Ret, &HFFFFFFFF
DProcess
Loop

EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum