Краткая версия:
Я написал небольшую программу для печати отчета. Эта программа исполняется с нашего проприетарного сервера, который работает как служба Windows. Он работает в нашей среде разработки, но не в сети наших клиентов.
Когда я пытаюсь получить доступ к системе печати в случае сбоя, я получаю сообщение об ошибке «Нет принтера по умолчанию». Мы попросили клиента создать новую учетную запись для входа в систему, для которой был определен принтер по умолчанию, и перезапустить сервер, используя эту учетную запись для входа. Та же ошибка
Обратите внимание, что эта ошибка генерируется при попытке найти указанный принтер, а не при попытке печати на нем.
Есть ли способ убедить порожденный сервером процесс в том, что принтеры действительно существуют?
Длинная версия:
In our "current" production environment, we have the following:
1. a proprietary server that runs as a service under windows.
2. a desktop client
--> accesses data via that service
--> uses fastreport4 to generate reports
--> developed using C++Builder6 (and VCL)
3. a PocketPC-based application that runs on scanning devices
--> uses Apache to communicate with the service
--> also uses Apache to poke a cgi-bin application that will bring up
the desktop app in stealth mode, run a report, and print it.
Мне было поручено заново реализовать функциональность Pocket-PC на устройстве сканирования на базе Android и удалить слой Apache из архитектуры. В приложении для Android я написал коммуникационный слой для прямого доступа к серверу (службе) (так же, как это делает рабочий стол). Сервер имеет возможность выполнять приложения, поэтому я написал небольшую для сбора данных и вызова fastreport для форматирования и печати.
Работает отлично. Нет проблем. … в нашей среде разработки. Он работает в нашей офисной сети с сервером, работающим в системе Windows 7. Он работает при запуске из командной строки на сервере Windows 2008 нашего клиента. Не работает при запуске из сервиса на сервере клиента.
Итак, вот код. В моей текущей ревизии у меня есть операторы try / catch и debug для печати (почти) каждой строки кода. Я удалил их для удобства чтения.
bool __fastcall TFormReportRunner::mySetPrinter(const char* name)
{
char pDevice[MAX_PATH];
char pDriver[MAX_PATH];
char APort[100];
UINT ADeviceMode;
bool printerFound = false;
bool errorFound = false;
String PrinterPort = String(name).UpperCase();
TPrinter* Prntr;
// I added this bit to see if it helps. Seems to make no difference
bool rc = SetDefaultPrinter("");
Prntr = Printer();
if (Prntr == NULL)
{
LogErrorMsg("Printer() returned null.");
return false;
}
int i = Prntr->Printers->Count - 1;
for (; i >= 0; i--)
{
// In the failing case, this next statement is the one that causes an exception.
Prntr->PrinterIndex = i;
Prntr->GetPrinter(pDevice, pDriver, APort, ADeviceMode);
DWORD SizeNeeded = 0;
HANDLE PrinterHandle;
if (OpenPrinter(pDevice, &PrinterHandle, NULL) == 0)
{
LogErrorMsg("Could not open printer");
return false;
}
GetPrinter(PrinterHandle, 2, NULL, 0, &SizeNeeded);
if (SizeNeeded == 0)
{
ClosePrinter(PrinterHandle);
LogErrorMsg("Could not retrieve printer info size");
return false;
}
PRINTER_INFO_2 PrinterInfo2;
char* buffer = new char[SizeNeeded];
if (GetPrinter(PrinterHandle, 2, buffer, SizeNeeded, &SizeNeeded) == 0)
{
ClosePrinter(PrinterHandle);
delete [] buffer;
LogErrorMsg("Could not retrieve printer info");
return false;
}
String PortName = ((PRINTER_INFO_2*)buffer)->pPortName;
delete [] buffer;
ClosePrinter(PrinterHandle);
if (PrinterPort == PortName)
{
frxReport1->PrintOptions->Printer = pDevice;
break;
}
}
Prntr->PrinterIndex = i;
return true;
}
Один из ИТ-специалистов заказчика говорит, что для того, чтобы работала версия Apache, ему необходимо запустить Apache от имени администратора с определенным принтером по умолчанию и зарегистрировать учетную запись администратора для печати. Он подозревает, что если мы запустим наш сервис в той же конфигурации, он начнет работать. Я не смог повторить это в нашей сети. Моя учетная запись администратора всегда работает независимо от того, вошел ли кто-либо в систему в данный момент или нет. Но это Windows 7 / Professional, а не серверная версия.
Это должно быть возможно … Апач делает это. Если он сможет найти принтер и распечатать его, я смогу, верно?
Любые советы или помощь будут с благодарностью. Что-нибудь. В самом деле. 🙂
Спасибо,
-Karen
Изменить: Добавление кода на стороне сервера.
Сначала пара заметок. Во-первых, это было написано 20 лет назад (кем-то другим). Во-вторых, это НЕ приложение VCL (и было скомпилировано с помощью компилятора Microsoft). Три, мы не будем это менять. Перекомпиляция с текущими компиляторами СЛИШКОМ опасна для чего-то, что иначе работает.
int myServer::RunProcess2(ClientCall *call, vector<string> &args, vector<string> &env, const char* input, unsigned int insize,
string *output, string *error)
{
CancelProcess2(); // only one process allowed per connection
string cmdline;
for (unsigned int i = 0; i < args.size(); i++)
{
if (i != 0)
cmdline += ' ';
cmdline += args[i];
}
env.push_back(EnvPATH);
int size = 1;
for (unsigned int i = 0; i < env.size(); i++)
{
size += env[i].size() + 1;
}
char *environment = (char*)malloc(size);
if (environment == NULL)
{
call->error = "Could not allocate memory for process environment variables";
return 0;
}
char *ptr = environment;
for (unsigned int i = 0; i < env.size(); i++)
{
size = env[i].size() + 1;
memcpy(ptr, env[i].c_str(), size);
ptr += size;
}
ptr[0] = '\0';
HANDLE hInputReadPipe = NULL, hInputWritePipe = NULL;
HANDLE hReadPipe, hWritePipe;
HANDLE hErrorReadPipe, hErrorWritePipe;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = true;
// create output pipe
if (CreatePipe(&hReadPipe, &hWritePipe, &sa, 4096) == 0)
{
free(environment);
call->error = "Error creation Pipe";
return 0;
}
// create error pipe
if (CreatePipe(&hErrorReadPipe, &hErrorWritePipe, &sa, 4096) == 0)
{
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
free(environment);
call->error = "Error creating Pipe";
return 0;
}
if (insize > 0)
{
// create input pipe
if (CreatePipe(&hInputReadPipe, &hInputWritePipe, &sa, 4096) == 0)
{
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
CloseHandle(hErrorReadPipe);
CloseHandle(hErrorWritePipe);
free(environment);
call->error = "Error creating Pipe";
return 0;
}
}
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = hWritePipe;
si.hStdError = hErrorWritePipe;
si.hStdInput = hInputReadPipe;
PROCESS_INFORMATION pi;
if (CreateProcess(NULL, (char*)cmdline.c_str(), NULL, NULL, true, 0, environment, NULL, &si, &pi) == 0)
{
CloseHandle(hErrorReadPipe);
CloseHandle(hErrorWritePipe);
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
if (hInputReadPipe != NULL)
{
CloseHandle(hInputReadPipe);
CloseHandle(hInputWritePipe);
}
free(environment);
call->error = string("Error executing command: ") + cmdline + "\n" + GetErrorText();
return 0;
}
report_handle = pi.hProcess;
CloseHandle(hErrorWritePipe);
CloseHandle(hWritePipe);
if (hErrorReadPipe != NULL)
CloseHandle(hInputReadPipe);
char buffer[4097];
DWORD BytesAvail;
DWORD BytesRead = 0;
DWORD BytesWritten = 0;
DWORD BytesToRead = sizeof(buffer) - 1;
DWORD BytesToWrite = insize;
bool finished_readpipe = false, finished_errorpipe = false;
bool finished_inputpipe = (insize == 0);
int wait_time = 1;
bool error_occurred = false;
while (finished_readpipe == false || finished_errorpipe == false || finished_inputpipe == false)
{
if (finished_inputpipe == false)
{
if (BytesToWrite <= 0)
{
CloseHandle(hInputWritePipe);
hInputWritePipe = NULL;
finished_inputpipe = true;
continue;
}
BytesAvail = 1000;
/*if (PeekNamedPipe(hInputWritePipe, NULL, NULL, NULL, &BytesAvail, NULL) == 0)
{
DWORD temp = GetLastError();
// pipe has been closed
finished_inputpipe = true;
continue;
}*/
if (BytesAvail > 0)
{
if (BytesAvail > BytesToWrite)
BytesAvail = BytesToWrite;
if (WriteFile(hInputWritePipe, input, BytesAvail, &BytesWritten, NULL) == 0)
{
if (GetLastError() == ERROR_NO_DATA)
{
int a = 2; // Pipe was closed (normal exit path).
}
finished_inputpipe = true;
continue;
}
input += BytesWritten;
BytesToWrite -= BytesWritten;
if (BytesToWrite == 0)
{
finished_inputpipe = true;
}
continue;
}
}
if (finished_readpipe == false)
{
while (true)
{
if (PeekNamedPipe(hReadPipe, NULL, NULL, NULL, &BytesAvail, NULL) == 0)
{
// pipe has been closed
finished_readpipe = true;
break;
}
if (BytesAvail <= 0)
break;
if (BytesAvail > sizeof(buffer) - 1)
BytesAvail = sizeof(buffer) - 1;
if (ReadFile(hReadPipe, buffer, BytesAvail, &BytesRead, NULL) == 0)
{
finished_readpipe = true;
break;
}
if (BytesRead == 0)
{
finished_readpipe = true;
break;
}
buffer[BytesRead] = '\0';
*output += buffer;
if (output->length() >= MAX_PROCESS_OUTPUT)
{
finished_inputpipe = true;
finished_readpipe = true;
finished_errorpipe = true;
error_occurred = true;
call->error = "Output limit reached";
}
}
if (finished_readpipe == true)
continue;
}
if (finished_errorpipe == false)
{
while (true)
{
if (PeekNamedPipe(hErrorReadPipe, NULL, NULL, NULL, &BytesAvail, NULL) == 0)
{
// pipe has been closed
finished_errorpipe = true;
break;
}
if (BytesAvail <= 0)
break;
if (BytesAvail > sizeof(buffer) - 1)
BytesAvail = sizeof(buffer) - 1;
if (ReadFile(hErrorReadPipe, buffer, BytesAvail, &BytesRead, NULL) == 0)
{
finished_errorpipe = true;
break;
}
if (BytesRead == 0)
{
finished_errorpipe = true;
break;
}
buffer[BytesRead] = '\0';
*error += buffer;
if (error->length() >= MAX_PROCESS_OUTPUT)
{
finished_inputpipe = true;
finished_readpipe = true;
finished_errorpipe = true;
error_occurred = true;
call->error = "Error output limit reached";
}
}
if (finished_errorpipe == true)
continue;
}
// don't tie up the server
if (wait_time < 100)
wait_time++;
Sleep(wait_time);
}
if (error_occurred == false)
WaitForSingleObject(pi.hProcess, INFINITE);
process_mutex.lock();
report_handle = NULL;
process_mutex.unlock();
DWORD exit_code = 0;
GetExitCodeProcess(pi.hProcess, &exit_code);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(hReadPipe);
CloseHandle(hErrorReadPipe);
if (hInputWritePipe != NULL)
CloseHandle(hInputWritePipe);
free(environment);
return exit_code;
}
Задача ещё не решена.
Других решений пока нет …