The following code snippet sends PostScript content (saved in pBuf buffer) to a CutePDF printer:
if (OpenPrinter(printerName, &hPrinter, NULL))
{
DOC_INFO_1 di1;
di1.pDatatype = L"RAW";
di1.pDocName = L"Raw print document";
di1.pOutputFile = NULL;
StartDocPrinter(hPrinter, 1, (LPBYTE)&di1);
StartPagePrinter(hPrinter);
DWORD dwWritten = 0;
WritePrinter(hPrinter, pBuf, dwBufSize, &dwWritten);
EndPagePrinter(hPrinter);
EndDocPrinter(hPrinter);
}
During the execution of this code, a dialog appears where I specify the name of the output file (e.g. D:/out.pdf), after that the pdf file is generated. So far so good. The problems begin when I'm trying to avoid the filename specifying step by changing Line 4 of the snippet:
di1.pOutputFile = L"D:/out.pdf";
Such code doesn't show the dialog during its execution (as expected), but the result D:/out.pdf isn't a pdf file, it's a copy of the PostScript file sent to the printer (copy of the contents of pBuf buffer). PDF Writer behaves in the same way. Why do PDF printers behave in this way and how can I achieve the needed behaviour (generate PDF file without specifying its name in UI)?
The Windows print system behaves this way, because, to be blunt, that's how its supposed to behave. If you specify a filename at that point then the print system sends the output to that file. If you don't specify a filename then it proceeds to normal processing.
Normally you would send the printer driver output to a port, and in the case of PDF printers a custom port monitor would pick up the output (PostScript in this case) and process it further. For PDF printers they send the PostScript on to a process which converts the PostScript to PDF (almost always using Ghostscript, though the Adobe print to PDF tools work the same way).
If you want to alter the output of the PDF process (ie write it to a different file), then you need to alter the way the port monitor works, not the way the print subsystem works, which is what your code is currently doing. By setting a filename where you are, you are simply short-circuiting the process, never invoking the port monitor, which is why the 'save file' dialog does not appear, and why the output is PostScript.
There may be a way of specifying the output file documented for the specific PDF printer you are using. If not, then for open source products (and if GS is built in they should be GPL licensed) you can request a copy of the source code for the product and alter it to suit yourself.
Alternatively, you can pick up a copy of Ghostscript and RedMon (open source Port Monitor) and create your own tool for doing the same job.
Related
I have an inquiry in the process of printing the printer.
Below are three of my program descriptions.
[My Application info]
Hook the APIs related to printing, such as StartDoc, StartPage, ExtTextOut, EndPage, and EndDoc.
After checking the entire string obtained from ExtTextOut in EndDoc API during printing, when a specific string is detected, the printer is deleted and the approval page is viewed.
After receiving approval, I would like to reprint the existing printer.
(JOB_CONTROL_RESUME will not be used in my program. You must print again after deleting unconditionally.)
Don't show the dialog again when reprinting.
With all the property information of the print that was started at the beginning in the DocumentProperties API, the same print is restarted with that information.
[Current Re-Print Logic]
This is my current status.
If you delete Print and proceed with printing again (this is the logic to run StartDoc, StartPage, Endpage, EndDoc that was previously in progress after creating a new printer DC), in a specific document (textbox of Excel, etc.), normal output is not possible.
I checked MSDN and it was confirmed that there is no API to output a specific print name in Windows, so I wrote the code as above.
[PowerShell Command]
Also, for printing, I tried the following command using PowerShell to print several files (ppt, xls, doc ...etc) including txt files.
PowerShell > Start-Process -FilePath "C:\Users\DeveloperO\Desktop\1234.docx" -Verb print
The above command worked, but the result is not what I want because a new process has been started.
Starting a new process and starting a print doesn't work for me.
[Please Answer]
Question 0)
Is there a command to print an open process without starting the process using powershell?
(Currently, I know that only txt files can be printed.)
Question 1)
I've been working in C++ language, is there an API to programmatically request a printer using C++?
Question 2)
I've also tried the ScheduleJob API. But this keeps giving 3002 Error. (Cannot find spool file.)
The code for this is below, can you give me some advice?
int main()
{
BOOL bResult = FALSE;
HANDLE hPrinter = NULL;
DWORD dNeedNum = 0;
CHAR szFullPath[MAX_PATH] = { 0, };
DWORD size = 4096;
ADDJOB_INFO_1 * JobInfo = { 0, };
W2M(g_wszFullPath, szFullPath);
bResult = OpenPrinter("Printer Name", &hPrinter, NULL);
if (bResult == FALSE)
{
printf("Error = %d", GetLastError());
}
JobInfo = (ADDJOB_INFO_1)LocalAlloc((LMEM_FIXED / LMEM_ZEROINIT), size);
bResult = AddJob(hPrinter, 1, (BYTE)JobInfo, size, &dNeedNum);
if (bResult == FALSE)
{
printf("Error = %d", GetLastError());
}
ZeroMemory(JobInfo->Path, sizeof(JobInfo->Path));
StringCbCopy(JobInfo->Path, sizeof(szFullPath), szFullPath);
bResult = ScheduleJob(hPrinter, JobInfo->JobId);
if (bResult == FALSE)
{
printf("Error = %d", GetLastError()); // <- Error Point 3002 Error
}
LocalFree(JobInfo);
ClosePrinter(hPrinter);
}
This isn't an answer to your specific questions 1,2 and 3, but in your comment, you mention 'What I want is to print and existing file or document'.
In this case, the Win32 APIs will only print GDI or XPS documents*, which means if you have a word document you want to print, you'll need to either convert it to XPS first, or use Word interop to print it, where the Word application will convert it to XPS or GDI and print it.
Unfortunately, any simple Win32 APIs for printing XPS files have been deprecated and are noted to be removed in later versions of windows.
Therefore, the remaining root to printing an XPS document is using the Print Document API, which is unfortunately non-trivial to explain.
https://learn.microsoft.com/en-us/windows/win32/printdocs/printdocs-printing
The premise at a high level would be to:
Open your document to print as an xps package using CreatePackageFromStream(...)
Create a package target for your print job using CreateDocumentPackageTargetForPrintJob(...).
Get a package target and package writer for printing your document using GetPackageTarget(...) and GetXpsOMPackageWriter(...).
Enumerate the documents and pages in the xps package and print them to your package writer.
The steps above don't really do justice to the complexity of these APIs I'm afraid.
If you need to pass in settings, you'll need to:
Get the default devmode for the printer you're targeting.
Convert the devmode to a print ticket for that printer.
Update the settings in the print ticket.
Use the print ticket in the relevant package writer APIs.
As an alternative, if you can use a separate printing process, you could use the C# APIs which are a lot more simple, and take a path to a file and print it with a single call.
https://learn.microsoft.com/en-us/dotnet/api/system.printing.printqueue.addjob?redirectedfrom=MSDN&view=net-5.0#overloads
* Unless your using passthrough APIs, with a specific printer driver.
The company I'm working with has a program written in ye olde vb6, which is updated pretty frequently, and most clients run the executable from a mapped network drive. This actually has surprisingly few issues, the biggest of which is automatic updates. Currently the updater program (written in c++) renames the existing exe, then downloads and places the new version into the old version's place. This generally works fine, but in some environments it simply fails.
The solution is running this command from microsoft:
for /f "skip=4 tokens=1" %a in ('net files') do net files %a /close
This command closes all network files that are shared (well... most) and then the updater can replace the exe.
In C++ I can use the System(""); function to run that command, or I could redirect the output of net files, and iterate through the results looking for the particular file in question and run net file /close command to close them. But it would be much much nicer if there were winapi functions that have similar capabilities for better reliability and future safety.
Is there any way for me to programmatically find all network shared files and close relevant ones?
You can programmatically do what net file /close does. Just include lmshare.h and link to Netapi32.dll. You have two functions to use: NetFileEnum to enumerate all open network files (on a given computer) and NetFileClose to close them.
Quick (it assumes program is running on same server and there are not too many open connections, see last paragraph) and dirty (no error checking) example:
FILE_INFO_2* pFiles = NULL;
DWORD nRead = 0, nTotal = 0;
NetFileEnum(
NULL, // servername, NULL means localhost
"c:\\directory\\path", // basepath, directory where VB6 program is
NULL, // username, searches for all users
2, // level, we just need resource ID
(LPBYTE*)&pFiles, // bufptr, need to use a double pointer to get the buffer
MAX_PREFERRED_LENGTH, // prefmaxlen, collect as much as possible
&nRead, // entriesread, number of entries stored in pFiles
&nTotal, // totalentries, ignore this
NULL //resume_handle, ignore this
);
for (int i=0; i < nRead; ++i)
NetFileClose(NULL, pFiles[i].fi2_id);
NetApiBufferFree(pFiles);
Refer to MSDN for details about NetFileEnum and NetFileClose. Note that NetFileEnum may return ERROR_MORE_DATA if more data is available.
Well this time I'm trying to write a program in C which recover deleted files from a disk, it could be an external disk, I have an idea than i had used before on linux, it is to open the disk as a kind of file and scaning the Headers and file footers of everything within the disk, the point is I'm not sure if there's allow on windows to open a disk as an File, basiclly I have the logic how to develope this program, but I'm not sure how to implement it on windows, anybody can give me a hand with this?.
The code I used on linux to open a disk as a file was:
Edit: That was a sample of what I was using guys, it's just to give you an idea of what I was doing, the correct syntax I used was the next:
direccion = ui->linea->text().toLatin1().constData();
f = fopen(direccion,"rb");
I used QT creator on linux, and direccion variable was a TextField value which contained the file path of the disk through a button function that open a QFileDialog...
could I use it in windows as well?
Thank you before hand..
"The code I used on linux to open a disk as a file was:"
File *f = fopen("E:\", "rb");
I seriously doubt you ever got this code working on any linux system (or windows either).
You'll need to escape the backslash path delimiter, if it's presented in any string literal:
FILE* f = fopen("E:\\", "rb");
// ^
Also all that filesystem path style you are presenting to access a particular disk, is about accessing a windows file path/disk.
No linux file system has notion about drive characters, and the file path delimiter value used is '/', not '\\'.
To recover deleted files, you can't use fopen or fstream::open because the file was deleted. Check the return value from the function or test the stream state.
The way to recover deleted files is:
Get the Master File Table as raw data.
Search for the record containing a string similar to the deleted
filename.
Change the entry in the Master File Table to "undeleted".
Write the Master File Table back to the drive.
The above usually requires platform specific API, which is different on Linux and Windows platforms.
I am trying to get the content of a file using WinHTTP in C++. The file is a XML File and is generated by a executable on a server.
The code for init, connect and even read a file on the specified server address is working.
// Connect to internet.
m_hInternet = InternetOpen(L"HTTPRIP",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
// Check if worked.
if( !m_hInternet )
return;
// Connect to selected URL.
m_hUrl = InternetOpenUrlA(m_hInternet, strUrl.c_str(), NULL, 0, INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RESYNCHRONIZE, 0);
// Check if worked.
if( !m_hUrl )
return;
if( InternetReadFile(m_hUrl, buf, BUFFER_SIZE, &bytesread) && bytesread != 0 )
{
// Put into std::string.
strData = std::string(buf,buf+bytesread);
}
Now I want to update the file (same address). The server update the file at 50Hz and I want my code to be able to ReadFile only if it has been updated by the server. Can InternetReadFile do that kind of thing? Maybe with a FLAG but I didn't find a thing on MSDN.
Thanks for your help.
There is no way in the HTTP protocol for you directly do that, hence there is no such function in WinHTTP. The easiest solution might be to download the file and see if it's changed, if the file is relatively small, or if the file is large, let the server which writes the file, also write a timestamp, checksum or counter increment file next to it.
Then your code would download the checksum file, see if it's changed, and in that case download the original file.
Or another solution would be to put a timestamp or similar data in the beginning of the XML file, and stop downloading the file if the timestamp (or checksum) is not updated. (This comes with its own drawbacks of course, you may have to write your own parser.)
If HTTP server has a page with info (e.g. timestamp) on this file (no matters that a file is generated; the page may be generated too), you may examine this page.
As you know that server updates the file with (nearly) constant speed, your app may just use the timer.
P.S. I doubt if there's really a sense in reading some file 50 times every second.
I wish to print some text directly to a network printer from my c++ code (I am coding with xcode 4). I do know that everything on unix is a file and believe that it would not be impossible to redirect the text using fstream method in c++ to the printer device file. The only problem is I don't know the device file in /dev associated with my network printer.
Is it possible to achieve printing using fstream method? Something like
std::fstream printFile;
printFile.open("//PATH/TO/PRINTER/DEV", std::ios::out);
printFile << "This must go to printer" << std::endl;
printFile.close();
And, if so
How to obtain the file in /dev corresponding to a particular printer?
Thanks in advance,
Nikhil
Opening and writing directly to a file used to be possible back in the days of serial printers; however, this is not the approach available today.
The CUPS daemon provides print queuing, scheduling, and administrative interfaces on OS X and many other Unix systems. You can use the lp(1) or lpr(1) commands to print files. (The different commands come from different versions of print spoolers available in Unix systems over the years; one was derived from the BSD-sources and the other derived from the AT&T sources. For compatibility, CUPS provides both programs.)
You can probably achieve something like you were after with popen(3). In shell, it'd be something like:
echo hello | lp -
The - says to print from standard input.
I haven't tested this, but the popen(3) equivalent would probably look like this:
FILE *f = popen("lp -", "w");
if (!f)
exit(1);
fprintf(f, "output to the printer");
I recommend testing some inputs at the shell first to make sure that CUPS is prepared to handle the formatting of the content you intend to send. You might need to terminate lines with CRLF rather than just \n, otherwise the printer may "stair-step" the output. Or, if you're sending PDF or PS or PCL data, it'd be worthwhile testing that in the cheapest possible manner to make sure the print system works as you expect.