WIA 2.0 - IWiaDevMgr2::GetImgDlg() - How to declare/init the ppbstrFilePaths parameter - c++

I'm trying to implement some WIA 2.0 code in my VS2012 C++ library and have run into a problem with the IWiaDevMgr2::GetImageDlg call, specifically the ppbstrFilePaths paraneter. I'm not quite sure how to declare/initialize it.
From the documentation at http://msdn.microsoft.com/en-us/library/windows/desktop/aa359932(v=vs.85).aspx:
ppbstrFilePaths [in] TYPE - BSTR**
The address of a pointer to an array of paths for the scanned files. Initialize the pointer to point to an array of size zero (0) before IWiaDevMgr2::GetImageDlg is called. See Remarks.
I've tried all sorts of variations on declaring this with no success, for example:
// No scanner selection dialog, hr = E_OUTOFMEMORY
BSTR *files = new BSTR[0];
HRESULT hr = _pWiaDevMgr->GetImageDlg(0, NULL, *_parentHwnd, path, fileTemplate, numFiles, &files, &_pWiaItemRoot);
I've also tried things similar to this:
// No scanner selection dialog, hr = E_OUTOFMEMORY
BSTR **files = (BSTR**)CoTaskMemAlloc(0);
*files = new BSTR[0];
HRESULT hr = _pWiaDevMgr->GetImageDlg(0, NULL, *_parentHwnd, path, fileTemplate, &numFiles, files, &_pWiaItemRoot);
Can anyone point me in the right direction for declaring and initializing this BSTR**? I'm not a big C++ dev and pretty much guessing at this point.

Turns out I was on the right track with my first try:
CComBSTR path("D:\\TestWiaScan");
CComBSTR fileTemplate("FileName");
LONG numFiles = 0L;
BSTR *files = new BSTR[0];
HRESULT hr1 = _pWiaDevMgr->GetImageDlg(0, NULL, _parentHwnd, path, fileTemplate, &numFiles, &files, &_pWiaItemRoot);
if (files)
{
for(int i=0;i < numFiles;i++)
{
SysFreeString(files[i]);
}
}
CoTaskMemFree(files);
if (_pWiaItemRoot)
{
_pWiaItemRoot->Release();
_pWiaItemRoot = NULL;
}
The reason my first try wasn't working was because of issues with the BSTR parameters I was passing in. Using CComBSTR or SysAllocString resolved that.

Related

Printing different documents silently in 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.

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);

don't know how to use IShellWindows::Item correctly

I'm using VC6 on XP system.
The following is my code. It runs perfectly on my computer, but on other computers it seems that pisw->Item(v, &pidisp) doesn't equals to S_OK. Now I'm trying to figure out what's wrong here
IShellWindows *pisw;
if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL,
IID_IShellWindows, (void**)&pisw))) {
VARIANT v;
V_VT(&v) = VT_I4;
IDispatch *pidisp;
found = FALSE;
for (V_I4(&v) = 0; !found && pisw->Item(v, &pidisp) == S_OK; V_I4(&v)++) {
IWebBrowserApp *piwba;
if (SUCCEEDED(pidisp->QueryInterface(IID_IWebBrowserApp, (void**)&piwba))) {
// blablabla....do something..
}
So I changed some code to
...
IDispatch *pidisp;
hr = pisw->Item(v, &pidisp);
if (SUCCEEDED(hr))
{
for (V_I4(&v) = 0; !found ; V_I4(&v)++) {
IWebBrowserApp *piwba;
if (SUCCEEDED(pidisp->QueryInterface(IID_IWebBrowserApp, (void**)&piwba))) {
// blablabla....do something..
}
}
then the return value of hr becomes to 1. And it gets access violation errors when running to "pidisp->.." step. Can anyone help me?
The original code incorrectly tests the result of pisw->Item(v, &pidisp). Weird, because it does use the correct check later on.
The problem is that there are many success return values besides S_OK. Your fix is correct, you should use SUCCEEDED(hr), but you incorrectly moved the loop INSIDE the SUCCEEDED(hr) test. You should check SUCCEEDED(hr) for every value of V_I4(&v).
Your S_FALSE result is because you now call hr = pisw->Item(v, &pidisp); before the loop, which means v is uninitialized (garbage). Assume for a moment that its garbage value is 728365. S_FALSE means: the call succeeded, but there are less than 728365 windows.
MSDN IShellWindows::Item:
Return value Type: HRESULT S_FALSE (1) The specified window was not
found.
The item you are looking was not found, and you obviously don't get a valid pidisp. Trying to use it results - expectedly - in access violation.
You need to handle "item not found" case properly, and check your v argument as well.

Heap allocation error within static library

I have a working Visual Studio project.
I've created a static library with the files of the original project (except main.cpp), also
I've created a "tester" project (with the static lib linked to it) with only a main.cpp file from the original project.
Both compiles without any relevant error.
And tester runs appropriately.
But! At testing the "tester" I am getting a heap allocation error at a (not the first)
new[] operator invoked in a constructor implemented in the library.
That line working fine in the original project without any error.
The "little" version of the code:
//the staticlib
void test() {
manager* m = new manager;
m->open();
}
//....
class manager {
public:
open() {
PRAWINPUTDEVICELIST lDevList;
UINT lDevCount;
GetRawInputDeviceList(NULL, &lDevCount, sizeof(RAWINPUTDEVICELIST));
lDevList = (PRAWINPUTDEVICELIST) malloc(sizeof(RAWINPUTDEVICELIST)*lDevCount);
GetRawInputDeviceList(lDevList, &lDevCount, sizeof(RAWINPUTDEVICELIST));
if(lDevCount) {
for(UINT i = 0; i < lDevCount; i++) {
HIDP_CAPS mCaps;
PHIDP_BUTTON_CAPS mButtonCaps;
PHIDP_VALUE_CAPS mValueCaps;
UINT size;
GetRawInputDeviceInfo(lDevList[i].hDevice, RIDI_DEVICENAME, NULL, &size);
char* name = new char[size+1];
//just to be sure
memset(name, 0, size+1);
//surely sure
name[size] = '\0';
GetRawInputDeviceInfo(lDevList[i].hDevice, RIDI_DEVICENAME, name, &size);
HANDLE lDev = CreateFile((LPCWSTR)name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);;
PHIDP_PREPARSED_DATA lPrep;
HidD_GetPreparsedData(lDev, &lPrep);
HidP_GetCaps(lPrep, &mCaps);
if(mCaps.NumberInputButtonCaps) {
//crash is here below
//mCaps.NumberInputButtonCaps ~1
mButtonCaps = new HIDP_BUTTON_CAPS[mCaps.NumberInputButtonCaps];
HidP_GetButtonCaps(HidP_Input, mButtonCaps, &mCaps.NumberInputButtonCaps, lPrep);
}
if(mCaps.NumberInputValueCaps) {
//if the first "crash-line" is commented, then
//the crash is here
mValueCaps = new HIDP_VALUE_CAPS[mCaps.NumberInputValueCaps];
HidP_GetValueCaps(HidP_Input, mValueCaps, &mCaps.NumberInputValueCaps, lPrep);
}
CloseHandle(lDev);
}
}
}
};
//the app
test();
Where I am wrong? Is it a typical novice commission I am not afraid of?
Sorry for my English, and thanks ahead for your time!
The error is that you should be allocating wide chars when you call GetRawInputDeviceInfo. From the manual
RIDI_DEVICENAME 0x20000007
pData points to a string that contains the device name.
For this uiCommand only, the value in pcbSize is the character count
(not the byte count).
In other words you should write
wchar_t* name = new wchar_t[size];
GetRawInputDeviceInfo(lDevList[i].hDevice, RIDI_DEVICENAME, name, &size);
Just from reading the manual, I have no actual experience with this API, but it seems a likely explanation.
Add logic that checks for error return states on every Win32 call you make. Possibly one of them is failing and when you remedy that, the rest will work. Always check for and handle errors when you are using Win32 APIs.