TCHAR* pszBackupPath;
m_Edt_ExportPath.GetWindowText(pszBackupPath, dwcchBackupPath);
StrTrim(pszBackupPath, L" ");
StrTrim(pszBackupPath, L"\\"); //this line has issue
iRet = _tcslen(pszBackupPath);
boRet = PathIsNetworkPath(pszBackupPath);
if (FALSE == boRet)
{
// MessageBox with string "Entered path is network path.
}
boRet = PathIsDirectory(pszBackupPath);
if (FALSE == boRet)
{
// MessageBox with string "Entered path is not a valid directory.
}
This is a part of my code in MFC.
I am passing a network path from UI. But because of StrTrim(pszBackupPath, L"\\") "\\" get trimmed from start and end. But I want it to be trimmed from end only.
I don't know any direct API. Please suggest.
There is a simple function to do that: PathRemoveBackslash (or PathCchRemoveBackslash for Windows 8 and later).
Related
I want to capture the string value returned from a url. The url is called from a c++ program. For example, the url in a browser address window returns a string value to the browser screen. How to I capture that value if the url is called from a c++ program.
I tried popen but that didn't seem to work.
TCHAR psBuffer[128];
FILE* pPipe;
if ((pPipe = _popen("http://xxxx.com/kmver.php", "rt")) == NULL)
{
AfxMessageBox(_T("Can't access http://xxxx.com/kmver.php"));
}
else
{
fgets(psBuffer, 128, pPipe);
CString sVal = psBuffer;
_pclose(pPipe);
}
I am trying to read windows Master File Table (MFT) for fast enumeration of files. Till now I have seen two approaches to do this:
As suggested by Jeffrey Cooperstein and Jeffrey Richter using DeviceIoControl
Direct parsing of MFT as presented in some opensource tools and An NTFS Parser Lib
For my project I am focusing on the approach [1]. The problem I am facing is mostly related to execution time. Just to be clear, following is my system and development enviornment:
IDE - Visual Studio 2013
Language - C++
OS - Windows 7 Professional x64
32 Bit binaries are generated for C++ and .NET code.
Problem
I have compared the version mentioned in [1] (slightly modified) with a VB.NET implementation available on codeplex. The issue is if I uncomment the statement in Inner Loop the C++ code execution time increases by a factor of 7-8x. I haven't implemented the path matching in C++ code (which is available in the VB code).
Q1. Kindly suggest how to improve the performance of the C++ code.
Timings for enumerating C:\ drive on my machine:
C++ (with uncommented statement in inner loop) - 21 seconds
VB.NET (with additional path matching code) - 3.5 seconds
For more clarity following is the C++ and VB.NET snippets.
C++
bool FindAll()
{
if (m_hDrive == NULL) // Handle of, for example, "\\.\C:"
return false;
USN_JOURNAL_DATA ujd = {0};
DWORD cb = 0;
BOOL bRet = FALSE;
MFT_ENUM_DATA med = {0};
BYTE pData[sizeof(DWORDLONG) + 0x10000] = {0};
bRet = DeviceIoControl(m_hDrive, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &ujd, sizeof(USN_JOURNAL_DATA), &cb, NULL);
if (bRet == FALSE) return false;
med.StartFileReferenceNumber = 0;
med.LowUsn = 0;
med.HighUsn = ujd.NextUsn;
//Outer Loop
while (TRUE)
{
bRet = DeviceIoControl(m_hDrive, FSCTL_ENUM_USN_DATA, &med, sizeof(med), pData, sizeof(pData), &cb, NULL);
if (bRet == FALSE) {
break;
}
PUSN_RECORD pRecord = (PUSN_RECORD)&pData[sizeof(USN)];
//Inner Loop
while ((PBYTE)pRecord < (pData + cb))
{
tstring sz((LPCWSTR) ((PBYTE)pRecord + pRecord->FileNameOffset), pRecord->FileNameLength / sizeof(WCHAR));
bool isFile = ((pRecord->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY);
if (isFile) m_dwFiles++;
//m_nodes[pRecord->FileReferenceNumber] = new CNode(pRecord->ParentFileReferenceNumber, sz, isFile);
pRecord = (PUSN_RECORD)((PBYTE)pRecord + pRecord->RecordLength);
}
med.StartFileReferenceNumber = *(DWORDLONG *)pData;
}
return true;
}
Where m_nodes is defined as typedef std::map<DWORDLONG, CNode*> NodeMap;
VB.NET
Public Sub FindAllFiles(ByVal szDriveLetter As String, fFileFound As FileFound_Delegate, fProgress As Progress_Delegate, fMatch As IsMatch_Delegate)
Dim usnRecord As USN_RECORD
Dim mft As MFT_ENUM_DATA
Dim dwRetBytes As Integer
Dim cb As Integer
Dim dicFRNLookup As New Dictionary(Of Long, FSNode)
Dim bIsFile As Boolean
' This shouldn't be called more than once.
If m_Buffer.ToInt32 <> 0 Then
Console.WriteLine("invalid buffer")
Exit Sub
End If
' progress
If Not IsNothing(fProgress) Then fProgress.Invoke("Building file list.")
' Assign buffer size
m_BufferSize = 65536 '64KB
' Allocate a buffer to use for reading records.
m_Buffer = Marshal.AllocHGlobal(m_BufferSize)
' correct path
szDriveLetter = szDriveLetter.TrimEnd("\"c)
' Open the volume handle
m_hCJ = OpenVolume(szDriveLetter)
' Check if the volume handle is valid.
If m_hCJ = INVALID_HANDLE_VALUE Then
Console.WriteLine("Couldn't open handle to the volume.")
Cleanup()
Exit Sub
End If
mft.StartFileReferenceNumber = 0
mft.LowUsn = 0
mft.HighUsn = Long.MaxValue
Do
If DeviceIoControl(m_hCJ, FSCTL_ENUM_USN_DATA, mft, Marshal.SizeOf(mft), m_Buffer, m_BufferSize, dwRetBytes, IntPtr.Zero) Then
cb = dwRetBytes
' Pointer to the first record
Dim pUsnRecord As New IntPtr(m_Buffer.ToInt32() + 8)
While (dwRetBytes > 8)
' Copy pointer to USN_RECORD structure.
usnRecord = Marshal.PtrToStructure(pUsnRecord, usnRecord.GetType)
' The filename within the USN_RECORD.
Dim FileName As String = Marshal.PtrToStringUni(New IntPtr(pUsnRecord.ToInt32() + usnRecord.FileNameOffset), usnRecord.FileNameLength / 2)
'If Not FileName.StartsWith("$") Then
' use a delegate to determine if this file even matches our criteria
Dim bIsMatch As Boolean = True
If Not IsNothing(fMatch) Then fMatch.Invoke(FileName, usnRecord.FileAttributes, bIsMatch)
If bIsMatch Then
bIsFile = Not usnRecord.FileAttributes.HasFlag(FileAttribute.Directory)
dicFRNLookup.Add(usnRecord.FileReferenceNumber, New FSNode(usnRecord.FileReferenceNumber, usnRecord.ParentFileReferenceNumber, FileName, bIsFile))
End If
'End If
' Pointer to the next record in the buffer.
pUsnRecord = New IntPtr(pUsnRecord.ToInt32() + usnRecord.RecordLength)
dwRetBytes -= usnRecord.RecordLength
End While
' The first 8 bytes is always the start of the next USN.
mft.StartFileReferenceNumber = Marshal.ReadInt64(m_Buffer, 0)
Else
Exit Do
End If
Loop Until cb <= 8
If Not IsNothing(fProgress) Then fProgress.Invoke("Parsing file names.")
' Resolve all paths for Files
For Each oFSNode As FSNode In dicFRNLookup.Values.Where(Function(o) o.IsFile)
Dim sFullPath As String = oFSNode.FileName
Dim oParentFSNode As FSNode = oFSNode
While dicFRNLookup.TryGetValue(oParentFSNode.ParentFRN, oParentFSNode)
sFullPath = String.Concat(oParentFSNode.FileName, "\", sFullPath)
End While
sFullPath = String.Concat(szDriveLetter, "\", sFullPath)
If Not IsNothing(fFileFound) Then fFileFound.Invoke(sFullPath, 0)
Next
'// cleanup
Cleanup() '//Closes all the handles
If Not IsNothing(fProgress) Then fProgress.Invoke("Complete.")
End Sub
Where fFileFound is defined as follows:
Sub(s, l)
If s.ToLower.StartsWith(sSearchPath) Then
lCount += 1
lstFileNames.Add(s.ToLower) '// Dim lstFileNames As List(Of String)
End If
End Sub
Where FSNode & CNode has the following structure:
//C++ version
class CNode
{
public:
//DWORDLONG m_dwFRN;
DWORDLONG m_dwParentFRN;
tstring m_sFileName;
bool m_bIsFile;
public:
CNode(DWORDLONG dwParentFRN, tstring sFileName, bool bIsFile = false) :
m_dwParentFRN(dwParentFRN), m_sFileName(sFileName), m_bIsFile(bIsFile){
}
~CNode(){
}
};
Note - The VB.NET code spawns a new thread (needed as it has GUI), whereas, I am calling the c++ function in the main thread (a simple console application for testing).
Update
It was a silly mistake from my side. The DeviceIoControl API is working as expected. Though the Debug build is a bit slower than the Release build. Refer to the following article:
how-can-i-increase-the-performance-in-a-map-lookup-with-key-type-stdstring
I didn't run your code, but since you say the commented line is the issue, the problem is probably the map insertion.
In the C++ code, you are using a std::map, which is implemented as a tree (sorted by key, log(n) access time).
In the VB code, you are using a Dictionary, which is implemented as a hash table (no sorting, constant access time).
Try using a std::unordered_map in the C++ version.
I am using this windows class as an input box for my choose player name window http://www.codeproject.com/Articles/13330/Using-Dialog-Templates-to-create-an-InputBox-in-C --I read the entire description provided with the codeproject submission but none of it describes how I can actually output the input from the box. Can anyone tell me how I can do this? For example, user inputs name 'bob'. Message box appears says "hey user your name is " ...
GetWindowText(GetDlgItem(..., ...), storage_var, 16);
How can I fill in these parameters assuming that is how you would get input using this windows class? What values would I put in there, how would this work? All help is appreciated, thanks!
EDIT
This is how I am calling it:
char buf[256] = " ";
WIN32INPUTBOX_PARAM param;
param.szTitle = "[RESOURCE] PING!";
param.szPrompt = "PONG!";
param.szResult = buf;
param.nResultSize = MAX_PATH;
param.DlgTemplateName = MAKEINTRESOURCE(101);
CWin32InputBox::InputBoxEx(¶m);
CWin32InputBox::InputBox("Testing", "enter detail"
, buf, 256, true);
Following that how would i get input into a variable from that input box?
As #RetiredNinja mentioned in his comment, you need to use the parameter, like
char buf[100] = "";
CWin32InputBox::InputBox("hello", "what?", buf, 100, false);
MessageBox(hDlg, buf, "You entered this", MB_OK);
EDIT
The code above is for a "straight" Win-API without MFC.
If you are using MFC, (please edit the question and add the mfc tag), the code would look like:
char buf[100] = "";
CWin32InputBox::InputBox("hello", "what?", buf, 100, false);
MessageBox(buf, "You entered this", MB_OK);
I want to achieve Path SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs
and key name : C:\Program Files\Common Files\Intel\WirelessCommon\TraceAPI.dll
Now I want to remove this key. Below is my attempt. Thanks for reading.
I have tried with Qt and with native API also. Qt remove is not working because of slash in keyname. In native API I am able to open key but when I'm trying to delete the key there is
ERROR_FILE_NOT_FOUND . I have tried all possible combination of key with slashes.
void sharedDlls(QSettings::Format format)
{
QSettings settings("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs", format);
HKEY hkey;
LONG n = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs"),
0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &hkey);
foreach (QString subkey, settings.allKeys() )
{
//converting to double quoted slashes
subkey = subkey.replace("/","\\\\");
if(clean)
{
// because of a slash it's impossible for qsettings to delete
settings.remove(subkey);
LONG n = RegDeleteKeyEx(hkey, (LPCWSTR)subkey.utf16(), KEY_WOW64_32KEY, 0);
if(n != ERROR_SUCCESS)
{
qDebug() << GetLastError();
qDebug() << "deleting error" << n;
//here is error
//ERROR_FILE_NOT_FOUND
}
}
}
//now close opened
::RegCloseKey(hkey);
}
I found no answer of my Qs so I'm adding solution..you can give bounty if like it ;)
It's workaround and not exact solution but I think it's good and stable.
There are two parts of answer
1. Handle 32 redirection
2. REG DELETE - to remove key containing backslashes
Part 1
Run 32
%windir%\SysWOW64\reg.exe
Run64
%windir%\sysnative\reg.exe
if you use system32 it will redirect to 32 bit reg when used from 32 bit app so avoid using it. Use sysnative. or disable redirection for temporary
SysNative is valid vista above only
Part 2 : This is complete solution (Qt)
QString reg32 = qgetenv("windir")+"\\SysWOW64\\reg.exe";
QString reg64 = qgetenv("windir")+ "\\sysnative\\reg.exe";
QString command = tr("%1 DELETE \"HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs\" /v \"%2\" /f")
.arg( is32Bit == true ? reg32 : reg64 )
.arg(key);
QProcess *process = new QProcess(this);
process->start(command);
if (process->waitForStarted())
{
process->waitForFinished();
QString l = process->readAllStandardOutput().simplified();
Logger::Log_Info("RegistryCleaner", command);
if(l.isEmpty())
Logger::Log_Error("RegistryCleaner", "Try again." );
else
Logger::Log_Info("RegistryCleaner", "Successfully done" );
}
else
qDebug() << process->readAllStandardError().simplified();
delete process;
I wanted to validate a file name along with its full path. I tried certain Regular Expressions as below but none of them worked correctly.
^(?:[\w]\:|\\)(\\[a-z_\-\s0-9\.]+)+\.(txt|gif|pdf|doc|docx|xls|xlsx)$
and
^(([a-zA-Z]\:)|(\\))(\\{1}|((\\{1})[^\\]([^/:*?<>""|]*))+)$
etc...
My requirement is as mentioned below:
Lets say if the file name is "c:\Demo.txt" then it should check every possibilites like no double slash should be included(c:\\Demo\\demo.text) no extra colon like(c::\Demo\demo.text). Should accept UNC files like(\\staging\servers) and others validation as well. Please help. I am really stuck here.
Why are you not using the File class ?
Always use it !
File f = null;
string sPathToTest = "C:\Test.txt";
try{
f = new File(sPathToTest );
}catch(Exception e){
Console.WriteLine(string.Format("The file \"{0}\" is not a valid path, Error : {1}.", sPathToTest , e.Message);
}
MSDN : http://msdn.microsoft.com/en-gb/library/system.io.file%28v=vs.80%29.aspx
Maybe you're just looking for File.Exists ( http://msdn.microsoft.com/en-gb/library/system.io.file.exists%28v=vs.80%29.aspx )
Also take a look to the Path class ( http://msdn.microsoft.com/en-us/library/system.io.path.aspx )
The GetAbsolutePath could be one way to get what you want! ( http://msdn.microsoft.com/en-us/library/system.io.path.getfullpath.aspx )
string sPathToTest = "C:\Test.txt";
string sAbsolutePath = "";
try{
sAbsolutePath = Path.GetAbsolutePath(sPathToTest);
if(!string.IsNullOrEmpty(sAbsolutePath)){
Console.WriteLine("Path valid");
}else{
Console.WriteLine("Bad path");
}
}catch(Exception e){
Console.WriteLine(string.Format("The file \"{0}\" is not a valid path, Error : {1}.", sPathToTest , e.Message);
}
If you are interested only in the filename part (and not the whole path because you get the file via upload) then you could try something like this:
string uploadedName = #"XX:\dem<<-***\demo.txt";
int pos = uploadedName.LastIndexOf("\\");
if(pos > -1)
uploadedName = uploadedName.Substring(pos+1);
var c = Path.GetInvalidFileNameChars();
if(uploadedName.IndexOfAny(c) != -1)
Console.WriteLine("Invalid name");
else
Console.WriteLine("Acceptable name");
This will avoid the use of Exceptions as method to drive the logic of your code.