Printing different documents silently in C++ - c++

I have folder of different documents like: pdf, txt, rtf, images.
My case is to send all documents to the printer (print it). Used framework is MFC and WinAPI. Current implementation has dialog box for choose documents and another dialog for choose printer.
Then question appears, how to print it all? Do I need to convert every documents to PDF, then merge it and print one pdf document? I will appreciate any advice in that field.
void CMultipleDocsPrintTestDlg::OnBnClickedButton1()
{
TCHAR strFilter[] = { _T("Rule Profile (*.pdf)||") };
// Create buffer for file names.
const DWORD numberOfFileNames = 100;
const DWORD fileNameMaxLength = MAX_PATH + 1;
const DWORD bufferSize = (numberOfFileNames * fileNameMaxLength) + 1;
CFileDialog fileDlg(TRUE, _T("pdf"), NULL, OFN_ALLOWMULTISELECT, strFilter);
TCHAR* filenamesBuffer = new TCHAR[bufferSize];
// Initialize beginning and end of buffer.
filenamesBuffer[0] = NULL;
filenamesBuffer[bufferSize - 1] = NULL;
// Attach buffer to OPENFILENAME member.
fileDlg.GetOFN().lpstrFile = filenamesBuffer;
fileDlg.GetOFN().nMaxFile = bufferSize;
// Create array for file names.
CString fileNameArray[numberOfFileNames];
if (fileDlg.DoModal() == IDOK)
{
// Retrieve file name(s).
POSITION fileNamesPosition = fileDlg.GetStartPosition();
int iCtr = 0;
while (fileNamesPosition != NULL)
{
fileNameArray[iCtr++] = fileDlg.GetNextPathName(fileNamesPosition);
}
}
// Release file names buffer.
delete[] filenamesBuffer;
CPrintDialog dlg(FALSE);
dlg.m_pd.Flags |= PD_PRINTSETUP;
CString printerName;
if (dlg.DoModal() == IDOK)
{
printerName = dlg.GetDeviceName();
}
// What next ???
}

You could make use of ShellExecute to do this. The parameter lpOperation can be set to print. To quote:
Prints the file specified by lpFile. If lpFile is not a document file, the function fails.
As mentioned in a similar discussion here on StackOverflow (ShellExecute, "Print") you should keep in mind:
You need to make sure that the machine's associations are configured to handle the print verb.
You referred to pdf, txt, rtf, images which should all be supported I would think by this mechanism.
ShellExecute(NULL, "print", fileNameArray[0], nullptr, nullptr, SW_SHOWNORMAL);
The last parameter might have to be changed (SW_SHOWNORMAL). This code would be put in a loop so you could call it for each file. And note that the above code snippet has not been tested.

Related

Append to registry without expanding variables

I'll just start off by saying that I'm by no means an expert in C++, so any pointers/tips are greatly appreciated.
I'm having some difficulties reading and writing from registry, while keeping variables, i.e. not expanding them.
I'm trying to append my executable path to the PATH environment variable (permanently), but I'm running into all sorts of problems.
I have a long PATH variable that makes it impossible to edit without using a program or regedit, so I opted to create an "OldPath" variable with my current PATH variable, and change my PATH variable to %OldPath%. This has worked great, but now when I try to write to it with C++, %OldPath% gets expanded into the old path variable and as a result, the variable gets truncated.
I tried first with normal strings, but I ended up with what looked like Chinese symbols in my PATH variable, so I changed it to wstring. Now I get normal strings, but the string gets truncated at 1172 characters.
My desired end result is that PATH is set to %OldPath;<current_path>
get_path_env()
inline std::wstring get_path_env()
{
wchar_t* buf = nullptr;
size_t sz = 0;
if (_wdupenv_s(&buf, &sz, L"PATH") == 0 && buf != nullptr)
{
std::wstring path_env = buf;
free(buf);
return path_env;
}
return L"";
}
set_permanent_environment_variable()
inline bool set_permanent_environment_variable()
{
const std::wstring path_env = get_path_env();
if (path_env == L"")
{
return false;
}
std::wstringstream wss;
wss << path_env;
if (path_env.back() != ';')
{
wss << L';';
}
wss << std::filesystem::current_path().wstring() << L'\0';
const std::wstring temp_data = wss.str();
HKEY h_key;
const auto key_path = TEXT(R"(System\CurrentControlSet\Control\Session Manager\Environment)");
if (const auto l_open_status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key_path, 0, KEY_ALL_ACCESS, &h_key); l_open_status == ERROR_SUCCESS)
{
const auto data = temp_data.c_str();
const DWORD data_size = static_cast<DWORD>(lstrlenW(data) + 1);
// ReSharper disable once CppCStyleCast
const auto l_set_status = RegSetValueExW(h_key, L"PATH", 0, REG_EXPAND_SZ, (LPBYTE)data, data_size);
RegCloseKey(h_key);
if (l_set_status == ERROR_SUCCESS)
{
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>("Environment"), SMTO_BLOCK, 100, nullptr);
return true;
}
}
return false;
}
In other words, I want to find the equivalent of the following in C#:
var assemblyPath = Directory.GetParent(Assembly.GetEntryAssembly()!.Location).FullName;
var pathVariable = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine);
Environment.SetEnvironmentVariable("PATH", $"{pathVariable};{assemblyPath}", EnvironmentVariableTarget.Machine);
EDIT: I actually haven't tested if that code expands the value or not, but I want to do as the C# code states and if possible, not expand the variables in the path variable.
You are trying to change the PATH setting in the registry. So one would expect that you would get the current PATH setting from the registry, change it, and set the new PATH setting in the registry.
But you are not getting the PATH setting from the registry. You are getting the PATH variable from the environment instead. Why is that? The environment is controlled by the setting in the registry, but it's not that setting. In particular, you noticed that the environment variables set in the registry get expanded before they actually go into the environment.
It's like changing the wallpaper by taking a screenshot of the desktop, changing the screenshot, then setting it as the wallpaper, then asking how to remove the icons from the wallpaper.
The solution is to simply get the current unexpanded PATH setting from the registry instead of the expanded one from the environment.

How to get the current Tab Item name from CTabCtrl in MFC?

I am trying to get the text of the currently chosen tab in CTabCtrl.
int tabCurSel = currentTabCtrl->GetCurSel();
TCITEM tcItem;
tcItem.mask = TCIF_TEXT;
tcItem.cchTextMax = 256; //Do I need this?
CString tabCurrentCString;
currentTabCtrl->GetItem(tabCurSel, &tcItem);
tabCurrentCString = tcItem.pszText;
CT2A tabCurrentChar(tabCurrentCString);
std::string tabCurrentStr(tabCurrentChar);
return tabCurrentStr;
I clearly have some unnecessary string conversions and currently this returns a "Error reading characters of the string" in
tcItem.pszText;
How can I get the string from the CTabCtrl? I ultimately am trying to get an std::string but the main question is how to get the text from the tab.
tcItem.pszText is pointing to 0. To fill it with text, it has to point to a buffer before a call is made to GetItem:
Documentation for: CTabCtrl::GetItem
pszText
Pointer to a null-terminated string containing the tab text if the
structure contains information about a tab. If the structure is
receiving information, this member specifies the address of the buffer
that receives the tab text.
Example:
TCITEM tcItem { 0 };
tcItem.mask = TCIF_TEXT;
const int len = 256;
tcItem.cchTextMax = len;
TCHAR buf[len] = { 0 };
tcItem.pszText = buf;
currentTabCtrl->GetItem(tabCurSel, &tcItem);
Both tcItem.pszText and buf will point to the same text. Or use CString with CString::GetBuffer()/CString::ReleaseBuffer()
CString tabCurrentCString;
TCITEM tcItem;
tcItem.mask = TCIF_TEXT;
tcItem.cchTextMax = 256;
tcItem.pszText = tabCurrentCString.GetBuffer(tcItem.cchTextMax);
BOOL result = currentTabCtrl->GetItem(tabCurSel, &tcItem);
tabCurrentCString.ReleaseBuffer();
if (result)
MessageBox(tabCurrentCString); //success
It looks like you are using the recommended Unicode settings. Avoid converting UNICODE to ANSI (std::string). This conversion will work for Latin languages, most of the time, but it's not needed. You can use std::wstring if you need to use that in STL, or convert to UTF-8 if you want to send data to internet etc.
std::string str = CW2A(tabCurrentCString, CP_UTF8);

Splitting up wchar_t array/string into two different arrays/strings

I have searched around before and couldn't really find a clean cut answer about this subject, what I want in simple terms is this:
Drag and drop feature gives me a directory with the filename.
C:\Users\chaos\Desktop\Game Launcher V1.0.exe
I need to make this one string into two different strings so it would do this
Directory: C:\Users\chaos\Desktop\
FileName: Game Launcher V1.0.exe
I figured there was foruma for a loop to do this, so I was wondering what your input on this situation would be, I'd love to hear it. Thanks :)
I am on Windows using Visual Studio 2015. (UTF16)
Since you are on Windows, have a look at the PathRemoveFileSpec(), PathStripPath(), and PathFindFileName() functions, eg:
LPTSTR szFullFilename = ...; // value from drag&drop
int iLength = lstrlen(szFullFilename);
LPTSTR szDirectory = new TCHAR[iLength+1];
lstrcpy(szDirectory, szFullFilename);
PathRemoveFileSpec(szDirectory);
LPTSTR szFileName = new TCHAR[iLength+1];
lstrcpy(szFileName, szFullFilename);
PathStripPath(szFileName);
// use szDirectory and szFileName as needed...
delete[] szDirectory;
delete[] szFileName;
LPTSTR szFullFilename = ...; // value from drag&drop
int iLength = lstrlen(szFullFilename);
LPTSTR szDirectory = new TCHAR[iLength];
lstrcpy(szDirectory, szFullFilename);
LPTSTR szFileName = PathFindFileName(szDirectory);
if (szFileName != szDirectory)
*(szFileName-1) = 0;
// use szDirectory and szFileName as needed...
delete[] szDirectory;

Reading from a very large text file resource in C++

We have some data in a text file which is built into our executable as a custom resource to be read at runtime. The size of this text file is over 7 million characters.
I can successfully search for and locate strings within the resource which appear near the top of the text file, but when attempting to search for terms a few million characters down, strstr returns NULL indicating that the string cannot be found. Is there a limit to the length of a string literal that can be stored in a char* or the amount of data that can be stored in an embedded resource? Code is shown below
char* data = NULL;
HINSTANCE hInst = NULL;
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(IDR_TEXT_FILE1), "TESTRESOURCE");
if(NULL != hRes)
{
HGLOBAL hData = LoadResource(hInst, hRes);
if (hData)
{
DWORD dataSize = SizeofResource(hInst, hRes);
data = (char*)LockResource(hData);
}
else
break;
char* pkcSearchResult = strstr(data, "NumListDetails");
if ( pkcSearchResult != NULL )
{
// parse data
}
}
Thanks.
The problem might be the method you use for searching. strstr uses ANSI strings, and will terminate when it encounters a '\0' in the search domain.
You might use something like memstr (one of many implementations can be found here).
Do you get any output from GetLastError(), specifically after calling SizeofResource.
You can also check that dataSize > 0 to ensure an error hasn't occurred.
DWORD dataSize = SizeofResource(hInst, hRes);
if(dataSize > 0)
{
data = (char*)LockResource(hData);
}
else
{
//check error codes
}
MSDN Docs
The problem was null characters in the data which prematurely ended the char* variable. To get around this I just had to read the data into a void pointer then copy it into a dynamically created array.
DWORD dataSize = SizeofResource(hInst, hRes);
void* pvData = LockResource(hData);
char* pcData = new char[dataSize];
memcpy_s(pcData,strlen(pcData),pvData,dataSize);

How do you open a resource string in Visual C++ 2010?

I created a basic stringtable resource in Visual C++. I am trying to access that resource. However, my program can't seem to find the resource. Here:
int main(int argc, char* argv[])
{
HRSRC hRsrc;
hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDS_STRING102), RT_STRING);
if (hRsrc == NULL) {
printf("Not found\n");
} else {
printf("Found\n");
}
}
This program can't find the resource and always returns null.
I created a simple bitmap resource and this new program identifies that just fine. Here:
int main(int argc, char* argv[])
{
HRSRC hRsrc;
hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDB_BITMAP1), RT_BITMAP);
if (hRsrc == NULL) {
printf("Not found\n");
} else {
printf("Found\n");
}
}
This finds the bitmap.
Do stringtable resources get handled somehow differently?
Assuming you do not want to use LoadString() this should help...
Strings and string tables are indeed treated differently when using FindResource() and FindResourceEx(). From this KB article:
String resources are stored as blocks of strings. Each block can have
up to sixteen strings and represents the smallest granularity of
string resource that can be loaded/updated. Each block is identified
by an identifier (ID), starting with one (1). We use this ID when
calling the FindResource, LoadResource and UpdateResource functions.
A string with ID, nStringID, is located in the block with ID,
nBlockID, given by the following formula:
nBlockID = (nStringID / 16) + 1; // Note integer division.
The lower 4 bits of nStringID indicates which entry in the block contains the actual string. Once you have calculated the block ID to pass to FindResource() and the index in the block where the string exists you have to scan through it's contents to find the string you are looking for.
The following code should get you started.
const WCHAR *stringPtr;
WCHAR stringLen;
// Get the id of the string table block containing the target string
const DWORD blockID = (nID >> 4) + 1;
// Get the offset of teh target string in the block
const DWORD itemID = nID % 0x10;
// Find the resource
HRSRC hRes = FindResourceEx(
hInst,
RT_STRING,
MAKEINTRESOURCE(blockID),
wLanguage);
if (hRes)
{
HGLOBAL hBlock = LoadResource(hInst, hRes);
const WCHAR *tableDataBlock = reinterpret_cast<LPCWSTR>(LockResource(hBlock));
const DWORD tableBlockSize = SizeofResource(hInst, hRes);
DWORD searchOffset = 0;
DWORD stringIndex = 0;
// Search through the section for the appropriate entry.
// The first two bytes of each entry is the length of the string
// followed by the Unicode string itself. All strings entries
// are stored one after another with no padding.
while(searchOffset < tableBlockSize)
{
if (stringIndex == itemID)
{
// If the string has size. use it!
if (tableDataBlock[searchOffset] != 0x0000)
{
stringPtr = &tableDataBlock[searchOffset + 1];
stringLen = tableDataBlock[searchOffset];
}
// Nothing there -
else
{
stringPtr = NULL;
stringLen = 0;
}
// Done
break;
}
// Go to the next string in the table
searchOffset += tableDataBlock[searchOffset] + 1;
// Bump the index
stringIndex++;
}
}
You could use LoadString directly instead. Here's some text from the MSDN FindResource documentation...
An application can use FindResource to find any type of resource, but this function should be used only if the application must access the binary resource data by making subsequent calls to LoadResource and then to LockResource.
To use a resource immediately...
...use LoadString!
After 2 days of research I found this(it works!):
#include <atlstr.h>
......
ATL::CString str;
WORD LangID = MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT);
str.LoadString(NULL,IDS_STRING101, LangID);