NetFileEnum and C++ - c++

I don't know C++ at all. But I am trying to slightly alter a C++ project that someone created that shows you who has what files open on the network.
The project works perfectly after a bit of work getting it to work with the latest VS.
The list box is populated with Paths and usernames. The paths consist of paths and paths with filename at the end. For example
(just edited this line, missed the \ at the end)
C:\Data\Work\
C:\Data\Work\Accounting.xls
The alteration I'm having trouble doing is I wanted to add an If statement while its loading the listbox and not load any pathnames that end in "\". So the listbox ends up only showing actual paths to files. In the above example it would not add the first path to the listbox and would add the second.
Here is the code snippet that works except for the If statement. I'm having trouble wrapping my head around Strings, literals, LPCTSTR, wchar, etc. :)
typedef std::basic_string<TCHAR> tstring;
dwStatus = NetFileEnum((LPWSTR) ((LPCTSTR) strServer), NULL, NULL, 3, (LPBYTE *) &pBuffer, MAX_PREFERRED_LENGTH, &dwReadEntries, &dwTotalEntries, NULL);
if (NERR_Success == dwStatus)
{
for (dwIndex = 0, pCurrent = pBuffer; dwIndex < dwReadEntries; dwIndex++, pCurrent++)
{
tstring mystring = pCurrent->fi3_pathname;
//MessageBox(mystring.c_str()), NULL,MB_OK);
if (mystring[mystring.length() -1] != L'\'')
{
nItem = m_lcFiles.InsertItem(dwIndex, pCurrent->fi3_pathname);
m_lcFiles.SetItemText(nItem, 1, pCurrent->fi3_username);
}
}
NetApiBufferFree(pBuffer);
}

Doh, I figured it our. The \ character is a special character of course, the escape sequence. So I had to compare to "\".
Works now, thanks!

Related

Special Characters in File Paths on Windows (c++)

I seem to be stuck on special characters (like äöü) in windows file paths. They are legal names for folders (users can set them).
A part of my program has to be able to traverse the filesystem. When trying to enter a childfolder with the name 'öö' (testcase) I get the error that the directory does not exist.
I am rather sure that the problem is this 'line':
wstring newPath = oldPath + ffd.cFileName + L"\\";
From
void reevaluateJob(wstring newPath) {
WIN32_FIND_DATA ffd;
HANDLE findFileHandle = FindFirstFile((newPath + L"\*").c_str(), &ffd);
//skipping invalid case handling and loop
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if ((wcscmp(ffd.cFileName, L".") != 0) && (wcscmp(ffd.cFileName, L"..") != 0))
reevaluateJob(newPath + ffd.cFileName + L"\\"); //<=== new path here
} else {
//skipping file part
}
}
Because printing the new path (or ffd.cFileName as wstring) results in different characters. Is there another data type that doesn't have this problem?
EDIT:
This works totally fine as long as the folder names do not contain special characters (like äöü etc.).
As pointed out by #ArnonZilca, #define UNICODE solved half of the problem. Sadly not all windows functions stick to that rule. Some also want #define _UNICODE.
In the process of trying to fix the problem, I also changed my whole code to use WCHAR* instead of wstring. I assume (but cannot be 100% sure) that this does NOT make a difference.

CFile and CStdioFile Reading one byte at a time

Using C++ MFC with Visual Studio 2008, I am trying to using CFile or CStdioFile to read in the last line of a text document, store it, and then reprint it after the file has had text amended to it.
I have gotten that part working, the only problem is is that it is not dynamic, you have to manually created an offSet for however long the last line is. As such, I am trying to make a function that reads the last line until it finds a common element in all of the files this will be working with, and count how many bytes there were. This is what I have now for that:
int MeasureLastTag(CStdioFile* xmlFile)
{
TCHAR lastTag[1];
CString tagBracket = _T("");
xmlFile->Seek(0, CFile::end);
int count = 0;
while(tagBracket != _T("<")) //Go back two, read ahead one
{
xmlFile->Seek(-2, CFile::current);
xmlFile->Read(lastTag, 1);
tagBracket = lastTag;
count++;
}
return count;
}
However, this causes an infinite loop that I can't seem to shake. Any ideas on how to make it work?
Additional Information, this is a sample of the file.
<Station>
</Station>
I want it to read < /Station> until it gets to the <, counting along the way.
Changing TCHAR lastTag[1] to char lastTag[1] has solved the issue.

C++ SendMessage loop stop and end of line

Here is my dilemma. I have a program that uses SendMessage to get text from a chat program. Now when I did this in Visual Basic I just put the SendMessage in a loop and whenever the chat was updated so would my output.
This is not the case with C++. When I put the SendMessage in a loop it loops forever. Say the Chat is something like:
Apples
Bananas
Cheerios
Now lets say I run my program it finds the text and starts looping. Now in Visual Basic it would keep looping until it hit Cheerios and it would stop and wait until someone in the chat typed something else.
With C++ it would output:
Apples
Bananas
Cheerios
Apples
Bananas
Cheerios
...
And go on forever. Is there a way to stop this? I was thinking along the terms of EOF but that wouldn't help because as soon as it hit the last line it would go out of the loop and wouldn't pick up anything else that people typed in chat.
Here is the portion of the code that gets the text. Now not that I do have an loop that won't end until I set bLoop to True.
cout << " + found RichEdit20W window at: " << hwndRichEdit20W << endl << endl;
cout << "- get text " << endl;
bool bLoop = false;
int textLen = (int)SendMessage(hwndRichEdit20W, WM_GETTEXTLENGTH, 0, 0);
while (bLoop == false)
{
const int MAXSIZE = 32678;
wchar_t szBuf[MAXSIZE];
SendMessage(hwndRichEdit20W, WM_GETTEXT, (WPARAM)textLen, (LPARAM)szBuf);
wcout << szBuf;
}
Thanks for any help!
VB CODE UPDATE
This is how I did it in my VB program. This is for a game console and not a chat window but it should be the same concept right?
Note Do While PARENThwnd <> IntPtr.Zero The handle is never 0 so it will be an infinite loop.
Private Sub bwConsole_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bwConsole.DoWork
Dim PARENThwnd As IntPtr
Dim CHILDhwnd As IntPtr
PARENThwnd = FindWindow(Nothing, "ET Console")
If PARENThwnd = IntPtr.Zero Then
txtConsole.Text = "ET Console is not availble."
Else
Do While PARENThwnd <> IntPtr.Zero
CHILDhwnd = GetDlgItem(PARENThwnd, 100)
Dim Handle As IntPtr = Marshal.AllocHGlobal(32767)
Dim NumText As Integer = CInt(SendMessage(CType(CHILDhwnd, IntPtr), WM_GETTEXT, CType(32767 \ Marshal.SystemDefaultCharSize, IntPtr), Handle))
Dim Text As String = Marshal.PtrToStringAuto(Handle)
txtConsole.Text = Text
Marshal.FreeHGlobal(Handle)
txtConsole.SelectionStart = txtConsole.TextLength
txtConsole.ScrollToCaret()
rtxtDefinition.Text = ""
Call GetDefinitions(txtConsole.Text)
Loop
End If
End Sub
Also will not be able to answer any questions until later tonight. Off to work.
You never update your bLoop variable, causing the loop to run infinitely. If you want to break your loop, implement a loop termination condition that changes the value of bLoop.
A condensed version of your code does the following:
bool bLoop = false;
while ( bLoop == false ) {
// This loop never terminates, unless bLoop is set to true
}
It is somewhat hard to understand, why you are looping to begin with. I suppose you hope the SendMessage call would somehow guess that you don't want it to run, until some condition is met. There is no such magic built into functions.
To respond to text changes you should choose a different solution altogether: An event-based model. The supported way to implement this is through UI Automation.
The first thing you need to understand is that the two versions of the code are doing very different things with the contents of the chat window. The VisualBasic version is taking that text and stuffing it into a text control. This has the effect of replacing the existing text with the new text from the chat window so you never see duplicates. The C++ version outputs the text to the output stream (console) each time it retrieves it. Unlike the text control used in the VB version the output stream appends the text rather than replaces it.
To get around this you need to keep tract of the previous text retrieved from the chat window and only output the difference. This may mean appending new text from the chat or outputting the entire string. The code below maintains a string buffer and manages appending or replacing the history of the chat. It also creates a new string containing only the differences from the last change allowing you to easily deal with updates.
class ChatHistory
{
std::wstring history;
public:
std::wstring update(const std::wstring& newText)
{
const std::wstring::size_type historySize = history.size();
if(newText.compare(0, historySize, history.c_str()) == 0)
{
history.append(newText.c_str() + historySize, newText.size() - historySize);
return history.c_str() + historySize;
}
else
{
history = newText;
}
return newText;
}
};
Below are the necessary changes to your code to use it. I haven't had a chance to test it with your exact set up but it should work.
ChatHistory history;
while (bLoop == false)
{
const int MAXSIZE = 32678;
wchar_t szBuf[MAXSIZE];
SendMessage(hwndRichEdit20W, WM_GETTEXT, (WPARAM)textLen, (LPARAM)szBuf);
std::wcout << history.update(szBuf);
}
In both VBA and C++ that structure will cause an infinite loop.
It sounds like you want an infinite loop, but don't want an infinite print out of the contents of the buffer.
As #IInspectable has pointed out (thanks!) sendmessage will copy the contents of the chat program into the buffer at every iteration of the loop.
So, either purge the chat window or let the program know what content is new and needs to be printed.
I'm on a linux box at the moment, so can't test this very easily. Please let me know if the following works...
#include <cwchar> // so we can use wcschr function
const int MAXSIZE = 32678; // moved outside of the loop
wchar_t szBuf[MAXSIZE]; // moved outside of the loop
szBuf[0] = L'\0'; // set sentinel (wide NULL!)
wchar_t* end_printed_message = szBuf;
//pointer to the end of the content that has been printed
while (bLoop == false)
{
SendMessage(hwndRichEdit20W, WM_GETTEXT, (WPARAM)textLen, (LPARAM)szBuf);
wchar_t* end_message_buffer = wcschr(szBuf, L'\0'); // find end of the string using wchar version of strchr()
if(end_printed_message != end_message_buffer){ // if the buffer contains something new
wcout << end_printed_message; // print out the new stuff (start at old end)
end_printed_message = end_message_buffer; // update the pointer to the end of the content
}
}
I don't know what headers you've already included, hence the use of 'ol faithful <cstring>
As an aside (yes, I will still bang on about this) mixed use of (w)cout is generally bad and may cause problems.

PathGetArgs/PathRemoveArgs vs. CommandLineToArgvW - is there a difference?

I'm working on some path-parsing C++ code and I've been experimenting with a lot of the Windows APIs for this. Is there a difference between PathGetArgs/PathRemoveArgs and a slightly-massaged CommandLineToArgvW?
In other words, aside from length/cleanness, is this:
std::wstring StripFileArguments(std::wstring filePath)
{
WCHAR tempPath[MAX_PATH];
wcscpy(tempPath, filePath.c_str());
PathRemoveArgs(tempPath);
return tempPath;
}
different from this:
std::wstring StripFileArguments(std::wstring filePath)
{
LPWSTR* argList;
int argCount;
std::wstring tempPath;
argList = CommandLineToArgvW(filePath.c_str(), &argCount);
if (argCount > 0)
{
tempPath = argList[0]; //ignore any elements after the first because those are args, not the base app
LocalFree(argList);
return tempPath;
}
return filePath;
}
and is this
std::wstring GetFileArguments(std::wstring filePath)
{
WCHAR tempArgs[MAX_PATH];
wcscpy(tempArgs, filePath.c_str());
wcscpy(tempArgs, PathGetArgs(tempArgs));
return tempArgs;
}
different from
std::wstring GetFileArguments(std::wstring filePath)
{
LPWSTR* argList;
int argCount;
std::wstring tempArgs;
argList = CommandLineToArgvW(filePath.c_str(), &argCount);
for (int counter = 1; counter < argCount; counter++) //ignore the first element (counter = 0) because that's the base app, not args
{
tempArgs = tempArgs + TEXT(" ") + argList[counter];
}
LocalFree(argList);
return tempArgs;
}
? It looks to me like PathGetArgs/PathRemoveArgs just provide a cleaner, simpler special-case implementation of the CommandLineToArgvW parsing, but I'd like to know if there are any corner cases in which the APIs will behave differently.
The functions are similar but not exactly the same - mostly relating to how quoted strings are handled.
PathGetArgs returns a pointer to the first character following the first space in the input string. If a quote character is encountered before the first space, another quote is required before the function will start looking for spaces again. If no space is found the function returns a pointer to the end of the string.
PathRemoveArgs calls PathGetArgs and then uses the returned pointer to terminate the string. It will also strip a trailing space if the first space encountered happened to be at the end of the line.
CommandLineToArgvW takes the supplied string and splits it into an array. It uses spaces to delineate each item in the array. The first item in the array can be quoted to allow spaces. The second and subsequent items can also be quoted, but they support slightly more complex processing - arguments can also include embedded quotes by prepending them with a backslash. For example:
"c:\program files\my app\my app.exe" arg1 "argument 2" "arg \"number\" 3"
This would produce an array with four entries:
argv[0] - c:\program files\my app\my app.exe
argv[1] - arg1
argv[2] - argument 2
argv[3] - arg "number" 3
See the CommandLineToArgVW docs for a full description of the parsing rules, including how you can have embedded backslashes as well as quotes in the arguments.
Yes I've observed a different behaviour with the current SDK (VS2015 Update 3 + Windows 1607 Anniversary SDK with SDK version set to 8.1):
Calling CommandLineToArgvW with an empty lpCmdLine (what you get from wWinMain when no arguments were passed) returns the program path and filename, which will be split-up on every space. But this was not specified in the parameter, it must have done that itself but failed to think about ignoring spacing that path itself:
lpCmdLine = ""
argv[0] = C:\Program
argv[1] = Files\Vendor\MyProgram.exe
Calling CommandLineToArgvW with lpCmdLine containing parameters, does not include the program path and name, so works as expected (so long as there are no further spaces in the parameters...):
lpCmdLine = "One=1 Two=\"2\""
argv[0] = One=1
argv[1] = Two=2
Note it also strips any other quotes inside the parameters when passed.
CommandLineToArgvW doesn't like the first parameter in the format Text=\"Quoted spaces\" so if you try to pass lpCmdLine to it directly it incorrectly splits the key=value pairs if they have spaces:
lpCmdLine = "One=\"Number One\" Two=\"Number Two\""
argv[0] = One=\"Number
argv[1] = One\"
argv[2] = Two=\"Number
argv[3] = Two\"
It's kind of documented here:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx
But this kind of behaviour with spaces in the program path was not expected. It seems like a bug to me. I'd prefer the same data to be processed in both situations. Because if I really want the path to the executable I'd call GetCommandLineW() instead.
The only sensible consistent solution in my opinion is to totally ignore lpCmdLine and call GetCommandLineW(), pass the results to CommandLineToArgvW() then skip the first parameter if you are not interested in the program path. That way, all combinations are supported, i.e. path with and without spaces, parameters with nested quotes with and without spaces.
int argumentCount;
LPWSTR commandLine = GetCommandLineW();
LPWSTR *arguments = CommandLineToArgvW(commandLine, &argumentCount);

CString extract file path

Hey I'm trying to extract the file path but the problem is that I'm stuck in an infinite loop don't understand why. Please have a look at my code.
CString myString(_T("C:\\Documents and Settings\\admin\\Desktop\\Elite\\Elite\\IvrEngine\\dxxxB1C1.log"));
int pos = myString.Find(_T("\\"));
while (pos != -1)
{
pos = myString.Find(_T("\\"), pos); // it keeps returning 2
}
CString folderPath = myString.Mid(pos);
Now the problem is that, Find() returns 2 the first time I run, but then in the while loop it keeps returning 2, why is the function unable to find the rest '\' ? So now I'm in an infinite loop :(.
It sounds like Find includes the character at the position you give it when searching. So if you give it the position of a character that matches the search, then it will return that same position.
You probably need to change it to:
pos = myString.Find(_T("\\"), pos + 1);
your code will never work! When the while loop finished, the contend of pos can not be used.
Here is a solution which will work:
CString folderPath;
int pos = myString.ReverseFind('\\');
if (pos != -1)
{
folderPath = myString.Left(pos);
}
You can fix the code (see the pos + 1 answers) but I think that you should use _splitpath_s instead which was intended for this kind of operations.
CString::Find always returns the first occurence of the character you're searching for. So it keeps finding the the first "\\" which is at index 2 infinitely since you're searching from 2 which includes that "\\"
I can understand your initial implementation, as the behaviour of CString::Find() seem to have changed over time.
Take a look at the MSDN docs for MFC implementation shipped with VC6 here and at the current implementation here. Especially look at the differences of the description of the 2nd offset parameter.
The solution to your problem is, as already stated above, to add 1 to the search offset of the successive Find() calls. You can also search for single chars (or wchar_ts) like that:
myString.Find(_T('\\'), pos+1);
EDIT:
BTW, take a look at the Path* familly of functions exposed by the shlwapi.dll, declared in shlwapi.h. Especially the PathRemoveFileSpec function might be of interest to you.
in MFC, example to get folder which including executable file:
char ownPth[MAX_PATH];
// Will contain exe path
HMODULE hModule = GetModuleHandle(NULL);
if(NULL == hModule){
return __LINE__;
}
// When passing NULL to GetModuleHandle, it returns handle of exe itself
GetModuleFileName(hModule,ownPth, (sizeof(ownPth)));
modulePath = (LPCSTR)ownPth;
modulePath = modulePath.Left(modulePath.ReverseFind(_T('\\')));
return 0;