I'm pretty much trying to make a AddInputEvent but, after a month, can't find a way to turn a local "function from FunctionCallbackInfo"(i'll just call this argf) in to a Persistent Function so that garbage collection doesn't erase the pointers.
Most stakeoverflow threads and example code I can find just say to Cast argf with a Local Function; then to throw that in to a Persistent New. This results in a error: cannot convert 'v8::Local<v8::Function>' to 'v8::Function*'
here is the code, not completely sure why I can't convert it
class inputevnt_feld{
public:
char* call_on;
v8::Persistent<v8::Function> func;
};
int entvcount = -1;
vector<inputevnt_feld> event_calls; //this is pretty much a array of events that we can call later
// in js looks like this "AddInputEvent("string", function);"
void AddInputEvent( const v8::FunctionCallbackInfo<v8::Value>& args ) {
v8::HandleScope handle_scope(args.GetIsolate());
//gotta make sure that we ain't letting in some trojan horse that has nothing in it
if (args[1]->IsFunction() && args[0]->IsString()) {
inputevnt_feld newt;
//converts js string to char array
v8::String::Utf8Value str(args.GetIsolate(), args[0]);
const char* cstr = ToCString(str);
newt.call_on = (char*)cstr;
//here is where the problem is with function casting
v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(args[1]);
newt.func = v8::Persistent<v8::Function>::New(args.GetIsolate(), callback);
//push the new stuff in to even array
event_calls.push_back(newt);
//getting vector array size is too much for my smol brain
//so I'ma just do this myself
entvcount++;
//cout << event_calls[entvcount].call_on << endl; //debug
}
}
Most stakeoverflow threads and example code I can find just say to Cast argf with a Local Function; then to throw that in to a Persistent New
Yes, that's correct. If you know how to read it, the C++ type system is your friend for figuring out the details.
If you look at the definition of v8::PersistentBase<T>::New, you'll see that it takes a T* (for its template type T). If you look at the v8::Local<T> class, you'll see that a way to get a T* from it is to use its operator*. That leads to:
v8::Local<v8::Function> callback = ...Cast(args[1]);
... = v8::Persistent<v8::Function>::New(..., *callback);
Alternatively, you can use the Persistent constructor directly, and pass it the Local without dereferencing it first:
v8::Local<v8::Function> callback = ...Cast(args[1]);
... = v8::Persistent<v8::Function>(..., callback);
Both options are entirely equivalent. Personally I'd prefer the latter as it takes slightly fewer characters to spell out, but that's really the only difference.
(Your current code as posted does something else: it ignores the result of the cast and passes the original args[1] directly to Persistent::New -- that's not going to work.)
I'm trying to make a 'Save As' dialog with an event that would change the default path based on the type of file we choose from the filters combo box. The problem is, all the examples I've seen execute the code on result IDOK or IDCANCEL while I'd need the code to be executed while the dialog is still opened.
Also, is there any way to differentiate between what filter has been chosen if the filters have the same type? The GetFileExt() method just returns the extension but I have no way of telling if it was the first .my filter or the template .my filter.
I've seen something like LPOFNHOOKPROC but there was no example of how would I even use it and I'm not sure whether it would even solve my problem or not.
void CMyClass::OnFileOpen()
{
CString pathNam;
CString fileName;
TCHAR szFilters[]= _T("MyType Files (*.my)|*.my|Template MyType (*.my)|*.my||");
CFileDialog fileDlg(TRUE, _T("my"), _T("*.my"),
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilters);
if(fileDlg.DoModal() == IDOK)
{
pathName = fileDlg.GetPathName();
fileName = fileDlg.GetFileTitle();
}
}
EDIT:
I am now able to get the specific filter that's been chosen by getting the OFN and checking the nFilterIndex value. So the remaining problem is whether I can update the path based on the chosen file format?
EDIT2:
I've found the OnChangeType method and overloaded it in the subclass and it indeed executes the method and the code within, but when I try to update the file path I get an access violation:
void TFileDialogExt::OnTypeChange()
{
LPWSTR buff = L"C:\\TEST\\template.my";
if(m_pOFN->nFilterIndex == 2)
m_ofn.lpstrFile = buff;
}
Basically you have to subclass CFileDialog and handle its CFileDialog::OnTypeChange method.
But, as suggested by Microsoft: you'd better use a new Common Item Dialog instead.
I did some research about this and found some useful questions:
Programmatically pre-select using IFileDialog in C++
How to use SHCreateItemFromParsingName with names from the shell namespace?
Also, have a look at: SHCreateItemFromParsingName.
Here is a sample OnTypeChange handler:
void CMyFileDialog::OnTypeChange()
{
{
IFileOpenDialog* pfod = NULL;
HRESULT hr = (static_cast<IFileDialog*>(m_pIFileDialog))->QueryInterface(IID_PPV_ARGS(&pfod));
if (SUCCEEDED(hr))
{
IShellItem* psiInitialDir;
CString strFolder = L"d:\\";
hr = SHCreateItemFromParsingName(strFolder.GetString(), NULL, IID_PPV_ARGS(&psiInitialDir));
if(SUCCEEDED(hr))
{
pfod->SetFolder(psiInitialDir);
}
}
}
CFileDialog::OnTypeChange();
}
My code uses a hard coded path for testing purposes, but you should now be able to complete your code:
Determine which path you want to use based on the currently selected filter index.
Use similar logic as here to navigate to that folder.
The code below lists files that have extension .cfg and it works fine on Win32. But, on iOS if i have a file that a user named with caps for the extension (e.g. test.CFG) then i miss it. I found this post using Delphi that might work using TDirectory::TFilterPredicate but i don't know how to implement in C++Builder.
TStringDynArray list;
TSearchOption searchOption;
UnicodeString DocsPath;
int lenDocsFolder;
DocsPath = System::Ioutils::TPath::GetDocumentsPath();
lenDocsFolder = DocsPath.Length();
searchOption = TSearchOption::soTopDirectoryOnly;
try
{
list = TDirectory::GetFiles(DocsPath, "*.cfg", searchOption);
}
catch (...)
{
ShowMessage("Incorrect path or search mask");
return;
}
I suppose i can just run a *.cfg block of code followed by a *.CFG but i'm hoping there is a cleaner approach.
Sorry, but I'm not used to C++. But this applies to both C++ and Delphi.
You are calling:
TDirectory.GetFiles(
const Path, SearchPattern: string;
const SearchOption: TSearchOption): TStringDynArray;
If you instead call this overloaded version:
TDirectory.GetFiles(
const Path, SearchPattern: string;
const SearchOption: TSearchOption;
const Predicate: TFilterPredicate): TStringDynArray;
you should be able to get what you need.
The TFilterPredicate type is defined as:
TFilterPredicate = reference to function(
const Path: string;
const SearchRec: TSearchRec): Boolean;
and should be the correct way to override the way files are matched.
I tried the Using a Lambda Expression from the link Remy posted in comment. I got an E2188 Expression syntaxerror until i disabled the classic Borland compiler. The code works great for simple predicate (on both Win32 and iOS).
String ext(".cfg");
files = TDirectory::GetFiles(CalcPath,
[ext](const String Path, const System::Sysutils::TSearchRec &SearchRec) -> bool
{
return ExtractFileExt(SearchRec.Name) == ext;
});
Now, how do i modify the extension string to return results for both .cfg and .CFG at same time?
String ext(".cfg"); // works fine
String ext(".cfg;.CFG"); // finds nothing
I want to know the time when a disk is made offline by user. Is there a way to know this through WMI classes or other ways?
If you cannot find a way to do it through the Win32 API/WMI or other, I do know of an alternate way which you could look into as a last-resort.
What about using NtQueryVolumeInformationFile with the FileFsVolumeInformation class? You can do this to retrieve the data about the volume and then access the data through the FILE_FS_VOLUME_INFORMATION structure. This includes the creation time.
At the end of the post, I've left some resource links for you to read more on understanding this so you can finish it off the way you'd like to implement it; I do need to quickly address something important though, which is that the documentation will lead you to
an enum definition for the _FSINFOCLASS, but just by copy-pasting it from MSDN, it probably won't work. You need to set the first entry of the enum definition to 1 manually, otherwise it will mess up and NtQueryVolumeInformationFile will return an error status of STATUS_INVALID_INFO_CLASS (because the first entry will be identified as 0 and not 1 and then all the entries following it will be -1 to what they should be unless you manually set the = 1).
Here is the edited version which should work.
typedef enum _FSINFOCLASS {
FileFsVolumeInformation = 1,
FileFsLabelInformation,
FileFsSizeInformation,
FileFsDeviceInformation,
FileFsAttributeInformation,
FileFsControlInformation,
FileFsFullSizeInformation,
FileFsObjectIdInformation,
FileFsDriverPathInformation,
FileFsVolumeFlagsInformation,
FileFsSectorSizeInformation,
FileFsDataCopyInformation,
FileFsMetadataSizeInformation,
FileFsMaximumInformation
} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
Once you've opened a handle to the disk, you can call NtQueryVolumeInformationFile like this:
NTSTATUS NtStatus = 0;
HANDLE FileHandle = NULL;
IO_STATUS_BLOCK IoStatusBlock = { 0 };
FILE_FS_VOLUME_INFORMATION FsVolumeInformation = { 0 };
...
Open the handle to the disk here, and then check that you have a valid handle.
...
NtStatus = NtQueryVolumeInformationFile(FileHandle,
&IoStatusBlock,
&FsVolumeInformation,
sizeof(FILE_FS_VOLUME_INFORMATION),
FileFsVolumeInformation);
...
If NtStatus represents an NTSTATUS error code for success (e.g. STATUS_SUCCESS) then you can access the VolumeCreationTime (LARGE_INTEGER) field of the FILE_FS_VOLUME_INFORMATION structure with the FsVolumeInformation variable.
Your final task at this point will be using the LARGE_INTEGER field named VolumeCreationTime to gather proper time/date information. There are two links included at the end of the post which are focused on that topic, they should help you sort it out.
See the following for more information.
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntqueryvolumeinformationfile
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ne-wdm-_fsinfoclass
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddk/ns-ntddk-_file_fs_volume_information
https://msdn.microsoft.com/en-us/library/windows/desktop/ms724280.aspx
https://blogs.msdn.microsoft.com/joshpoley/2007/12/19/datetime-formats-and-conversions/
How would I compare 2 strings to determine if they refer to the same path in Win32 using C/C++?
While this will handle a lot of cases it misses some things:
_tcsicmp(szPath1, szPath2) == 0
For example:
forward slashes / backslashes
relative / absolute paths.
[Edit] Title changed to match an existing C# question.
Open both files with CreateFile, call GetFileInformationByHandle for both, and compare dwVolumeSerialNumber, nFileIndexLow, nFileIndexHigh. If all three are equal they both point to the same file:
GetFileInformationByHandle function
BY_HANDLE_FILE_INFORMATION Structure
Filesystem library
Since C++17 you can use the standard filesystem library. Include it using #include <filesystem>. You can access it even in older versions of C++, see footnote.
The function you are looking for is equivalent, under namespace std::filesystem:
bool std::filesystem::equivalent(const std::filesystem::path& p1, const filesystem::path& p2 );
To summarize from the documentation: this function takes two paths as parameters and returns true if they reference the same file or directory, false otherwise. There is also a noexcept overload that takes a third parameter: an std::error_code in which to save any possible error.
Example
#include <filesystem>
#include <iostream>
//...
int main() {
std::filesystem::path p1 = ".";
std::filesystem::path p2 = fs::current_path();
std::cout << std::filesystem::equivalent(p1, p2);
//...
}
Output:
1
Using filesystem before C++17
To use this library in versions prior to C++17 you have to enable experimental language features in your compiler and include the library in this way: #include <experimental/filesystem>. You can then use its functions under the namespace std::experimental::filesystem. Please note that the experimental filesystem library may differ from the C++17 one. See the documentation here.
For example:
#include <experimental/filesystem>
//...
std::experimental::filesystem::equivalent(p1, p2);
See this question: Best way to determine if two path reference to same file in C#
The question is about C#, but the answer is just the Win32 API call GetFileInformationByHandle.
use the GetFullPathName from kernel32.dll, this will give you the absolute path of the file. Then compare it against the other path that you have using a simple string compare
edit: code
TCHAR buffer1[1000];
TCHAR buffer2[1000];
TCHAR buffer3[1000];
TCHAR buffer4[1000];
GetFullPathName(TEXT("C:\\Temp\\..\\autoexec.bat"),1000,buffer1,NULL);
GetFullPathName(TEXT("C:\\autoexec.bat"),1000,buffer2,NULL);
GetFullPathName(TEXT("\\autoexec.bat"),1000,buffer3,NULL);
GetFullPathName(TEXT("C:/autoexec.bat"),1000,buffer4,NULL);
_tprintf(TEXT("Path1: %s\n"), buffer1);
_tprintf(TEXT("Path2: %s\n"), buffer2);
_tprintf(TEXT("Path3: %s\n"), buffer3);
_tprintf(TEXT("Path4: %s\n"), buffer4);
the code above will print the same path for all three path representations.. you might want to do a case insensitive search after that
A simple string comparison is not sufficient for comparing paths for equality. In windows it's quite possible for c:\foo\bar.txt and c:\temp\bar.txt to point to exactly the same file via symbolic and hard links in the file system.
Comparing paths properly essentially forces you to open both files and compare low level handle information. Any other method is going to have flaky results.
Check out this excellent post Lucian made on the subject. The code is in VB but it's pretty translatable to C/C++ as he PInvoke'd most of the methods.
http://blogs.msdn.com/vbteam/archive/2008/09/22/to-compare-two-filenames-lucian-wischik.aspx
Based on answers about GetFileInformationByHandle(), here is the code.
Note: This will only work if the file already exists...
//Determine if 2 paths point ot the same file...
//Note: This only works if the file exists
static bool IsSameFile(LPCWSTR szPath1, LPCWSTR szPath2)
{
//Validate the input
_ASSERT(szPath1 != NULL);
_ASSERT(szPath2 != NULL);
//Get file handles
HANDLE handle1 = ::CreateFileW(szPath1, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE handle2 = ::CreateFileW(szPath2, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
bool bResult = false;
//if we could open both paths...
if (handle1 != INVALID_HANDLE_VALUE && handle2 != INVALID_HANDLE_VALUE)
{
BY_HANDLE_FILE_INFORMATION fileInfo1;
BY_HANDLE_FILE_INFORMATION fileInfo2;
if (::GetFileInformationByHandle(handle1, &fileInfo1) && ::GetFileInformationByHandle(handle2, &fileInfo2))
{
//the paths are the same if they refer to the same file (fileindex) on the same volume (volume serial number)
bResult = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber &&
fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh &&
fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow;
}
}
//free the handles
if (handle1 != INVALID_HANDLE_VALUE )
{
::CloseHandle(handle1);
}
if (handle2 != INVALID_HANDLE_VALUE )
{
::CloseHandle(handle2);
}
//return the result
return bResult;
}
If you have access to the Boost libraries, try
bool boost::filesystem::path::equivalent( const path& p1, const path& p2 )
http://www.boost.org/doc/libs/1_53_0/libs/filesystem/doc/reference.html#equivalent
To summarize from the docs: Returns true if the given path objects resolve to the same file system entity, else false.
What you need to do is get the canonical path.
For each path you have ask the file system to convert to a canonical path or give you an identifier the uniquely identifies the file (such as the iNode).
Then compare the canonical path or the unique identifier.
Note:
Do not try and figure out the conical path yourself the File System can do things with symbolic links etc that that are not easily tractable unless you are very familiar with the filesystem.
Comparing the actual path strings will not produce accurate results if you refer to UNC or Canonical paths (i.e. anything other than a local path).
shlwapi.h has some Path Functions that may be of use to you in determing if your paths are the same.
It contains functions like PathIsRoot that could be used in a function of greater scope.
If the files exist and you can deal with the potential race condition and performance hit from opening the files, an imperfect solution that should work on any platform is to open one file for writing by itself, close it, and then open it for writing again after opening the other file for writing. Since write access should only be allowed to be exclusive, if you were able to open the first file for writing the first time but not the second time then chances are you blocked your own request when you tried to open both files.
(chances, of course, are also that some other part of the system has one of your files open)
Open both files and use GetFinalPathNameByHandle() against the HANDLEs. Then compare the paths.