Change folder attribute to hidden on iOS (FMX, C++) - firemonkey

I want to create a directory at runtime and make it hidden. Using this example i tried the following code and it works fine on Win32 but errors on iOS build:
UnicodeString TestPath;
TestPath = System::Ioutils::TPath::GetDocumentsPath() + "\\test\\";
TDirectory::CreateDirectory(TestPath);
TFileAttributes dirattribs;
dirattribs = TDirectory::GetAttributes(TestPath);
dirattribs = dirattribs << TFileAttribute::faHidden;
TDirectory::SetAttributes(TestPath, dirattribs);
The build error i get when building for iOS or Android is no member named 'faHidden' in 'System::Ioutils::TFileAttribute'. So, how can i change folder attributes on iOS and Android?
p.s. Using Rad Studio 10.3.2 (C++ Builder).

faHidden is not implement on Posix systems. This is documented behavior in Embarcadero's DocWiki. faHidden is only available on Windows.
To create a folder that is hidden from the user on Posix systems, you can simply prepend the folder name with a leading dot:
UnicodeString TestPath = System::Ioutils::TPath::GetDocumentsPath();
#ifdef _Windows
TestPath = System::Ioutils::TPath::Combine(TestPath, _D("test"));
TDirectory::CreateDirectory(TestPath);
TFileAttributes dirattribs = TDirectory::GetAttributes(TestPath);
dirattribs = dirattribs << TFileAttribute::faHidden;
TDirectory::SetAttributes(TestPath, dirattribs);
#else
TestPath = System::Ioutils::TPath::Combine(TestPath, _D(".test"));
TDirectory::CreateDirectory(TestPath);
#endif
Note, it is possible to create a folder with a leading dot on Windows, too. It just won't have an effect on the folder's hidden attribute, you would still have to set that explicitly:
UnicodeString TestPath = System::Ioutils::TPath::GetDocumentsPath();
TestPath = System::Ioutils::TPath::Combine(TestPath, _D(".test"));
TDirectory::CreateDirectory(TestPath);
#ifdef _Windows
TFileAttributes dirattribs = TDirectory::GetAttributes(TestPath);
dirattribs = dirattribs << TFileAttribute::faHidden;
TDirectory::SetAttributes(TestPath, dirattribs);
#endif

Related

QtDBus in .pro brokeCONFIG += force_debug_info?

I have a qmake/qtcreator c++ project, i set CONFIG += separate_debug_info force_debug_info in my pro file and myapp.debug was generated, today i add QT += dbus and debug file isn't generated.
I checked the diff with my last and ok backup, here it's
--- /home/eric/rpmbuild/SOURCES/QtVsPlayer-1.0.19/QtVsPlayer/QtVsPlayer.pro
+++ /home/eric/Projets/qt/QtVsPlayer/QtVsPlayer.pro
## -1,10 +1,10 ##
-VERSION = 1.0.19
+VERSION = 1.0.20
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
-QT += core gui opengl multimedia multimediawidgets
+QT += dbus core gui opengl multimedia multimediawidgets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
-CONFIG += c++11 link_prl
+CONFIG += c++11 link_prl static #separate_debug_info # debug_and_release
# Specifies name of the binary.
TARGET = QtVsPlayer
## -85,6 +85,9 ##
settingsform.ui \
videoctrls.ui
+DBUS_ADAPTORS += local.QtVsPlayer.xml
+DBUS_INTERFACES += local.QtVsPlayer.xml
+
TRANSLATIONS += \
QtVsPlayer_fr_FR.ts
Answer myself but not understand!
it is the bad boy!
with 
+CONFIG += static
debug file isn't generated!

Signing an appxbundle using CryptUIWizDigitalSign API

I'm facing a rather interesting issue in regards to Authenticode signing an UWP appxbundle file.
Some background:
The client provided us with a SafeNet USB token containing the signing certificate. The private key is not exportable, of course. I want to be able to use this certificate for our automated release builds to sign the package. Unfortunately, the token requires a PIN to be entered once per session, so for example if the build agent reboots, the build will fail. We enabled single login on the token so it's enough to unlock it once a session.
Current state:
We can use signtool on the appxbundle without any problems, given the token has been unlocked. This works well enough but breaks as soon as the machine is rebooted or the workstation is locked.
After some searching I managed to find this piece of code. This takes the signing parameters (including the token PIN) and invokes Windows API to sign the target file. I managed to compile this and it worked flawlessly for signing the installation wrapper (EXE file) - the token did not ask for PIN and was unlocked automatically by the API call.
However, when I invoked the same code on the appxbundle file, the call to CryptUIWizDigitalSign failed with error code 0x80080209 APPX_E_INVALID_SIP_CLIENT_DATA. This is a mystery to me because invoking signtool on the same bundle, with the same parameters/certificate works without problem so the certificate should be fully compatible with the package.
Does anyone have experience with something like this? Is there a way to figure out what is the root cause of the error (what is incompatible between my cert and the bundle)?
EDIT 1
In response to a comment:
The code I'm using to call the APIs (taken directly from the aforementioned SO question)
#include <windows.h>
#include <cryptuiapi.h>
#include <iostream>
#include <string>
#pragma comment (lib, "cryptui.lib")
const std::wstring ETOKEN_BASE_CRYPT_PROV_NAME = L"eToken Base Cryptographic Provider";
std::string utf16_to_utf8(const std::wstring& str)
{
if (str.empty())
{
return "";
}
auto utf8len = ::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), NULL, 0, NULL, NULL);
if (utf8len == 0)
{
return "";
}
std::string utf8Str;
utf8Str.resize(utf8len);
::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), &utf8Str[0], utf8Str.size(), NULL, NULL);
return utf8Str;
}
struct CryptProvHandle
{
HCRYPTPROV Handle = NULL;
CryptProvHandle(HCRYPTPROV handle = NULL) : Handle(handle) {}
~CryptProvHandle() { if (Handle) ::CryptReleaseContext(Handle, 0); }
};
HCRYPTPROV token_logon(const std::wstring& containerName, const std::string& tokenPin)
{
CryptProvHandle cryptProv;
if (!::CryptAcquireContext(&cryptProv.Handle, containerName.c_str(), ETOKEN_BASE_CRYPT_PROV_NAME.c_str(), PROV_RSA_FULL, CRYPT_SILENT))
{
std::wcerr << L"CryptAcquireContext failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return NULL;
}
if (!::CryptSetProvParam(cryptProv.Handle, PP_SIGNATURE_PIN, reinterpret_cast<const BYTE*>(tokenPin.c_str()), 0))
{
std::wcerr << L"CryptSetProvParam failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return NULL;
}
auto result = cryptProv.Handle;
cryptProv.Handle = NULL;
return result;
}
int wmain(int argc, wchar_t** argv)
{
if (argc < 6)
{
std::wcerr << L"usage: etokensign.exe <certificate file path> <private key container name> <token PIN> <timestamp URL> <path to file to sign>\n";
return 1;
}
const std::wstring certFile = argv[1];
const std::wstring containerName = argv[2];
const std::wstring tokenPin = argv[3];
const std::wstring timestampUrl = argv[4];
const std::wstring fileToSign = argv[5];
CryptProvHandle cryptProv = token_logon(containerName, utf16_to_utf8(tokenPin));
if (!cryptProv.Handle)
{
return 1;
}
CRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO extInfo = {};
extInfo.dwSize = sizeof(extInfo);
extInfo.pszHashAlg = szOID_NIST_sha256; // Use SHA256 instead of default SHA1
CRYPT_KEY_PROV_INFO keyProvInfo = {};
keyProvInfo.pwszContainerName = const_cast<wchar_t*>(containerName.c_str());
keyProvInfo.pwszProvName = const_cast<wchar_t*>(ETOKEN_BASE_CRYPT_PROV_NAME.c_str());
keyProvInfo.dwProvType = PROV_RSA_FULL;
CRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO pvkInfo = {};
pvkInfo.dwSize = sizeof(pvkInfo);
pvkInfo.pwszSigningCertFileName = const_cast<wchar_t*>(certFile.c_str());
pvkInfo.dwPvkChoice = CRYPTUI_WIZ_DIGITAL_SIGN_PVK_PROV;
pvkInfo.pPvkProvInfo = &keyProvInfo;
CRYPTUI_WIZ_DIGITAL_SIGN_INFO signInfo = {};
signInfo.dwSize = sizeof(signInfo);
signInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE;
signInfo.pwszFileName = fileToSign.c_str();
signInfo.dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_PVK;
signInfo.pSigningCertPvkInfo = &pvkInfo;
signInfo.pwszTimestampURL = timestampUrl.c_str();
signInfo.pSignExtInfo = &extInfo;
if (!::CryptUIWizDigitalSign(CRYPTUI_WIZ_NO_UI, NULL, NULL, &signInfo, NULL))
{
std::wcerr << L"CryptUIWizDigitalSign failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return 1;
}
std::wcout << L"Successfully signed " << fileToSign << L"\n";
return 0;
}
The certificate is a CER file (public portion only) exported from the token and the container name is taken from the token's info. As I mentioned, this works correctly for EXE files.
The signtool command
signtool sign /sha1 "cert thumbprint" /fd SHA256 /n "subject name" /t "http://timestamp.verisign.com/scripts/timestamp.dll" /debug "$path"
This also works, when I call it either manually or from the CI build when the token is unlocked. But the code above fails with the mentioned error.
EDIT 2
Thanks to all of you, I now have a working implementation! I ended up using the SignerSignEx2 API, as suggested by RbMm. This seems to work fine for both appx bundles and PE files (different parameters for each). Verified on Windows 10 with a TFS 2017 build agent - unlocks the token, finds a specified certificate in the cert store, and signs+timestamps the specified file.
I published the result on GitHub, if anyone is interested: https://github.com/mareklinka/SafeNetTokenSigner
first of all i look where CryptUIWizDigitalSign failed:
the CryptUIWizDigitalSign called SignerSignEx function, with pSipData == 0. for sign PE file (exe, dll, sys) - this is ok and will be work. but for appxbundle (zip archive file type) this parameter mandatory and must point to APPX_SIP_CLIENT_DATA: for appxbundle call stack is
CryptUIWizDigitalSign
SignerSignEx
HRESULT Appx::Packaging::AppxSipClientData::Initialize(SIP_SUBJECTINFO* subjectInfo)
at very begin of Appx::Packaging::AppxSipClientData::Initialize we can view next code:
if (!subjectInfo->pClientData) return APPX_E_INVALID_SIP_CLIENT_DATA;
this is exactly where your code fail.
instead of CryptUIWizDigitalSign need direct call SignerSignEx2 and pSipData is mandatory parameter in this case.
in msdn exist full worked example - How to programmatically sign an app package (C++)
the key point here:
APPX_SIP_CLIENT_DATA sipClientData = {};
sipClientData.pSignerParams = &signerParams;
signerParams.pSipData = &sipClientData;
the modern SignTool call SignerSignEx2 direct:
here again clear visible:
if (!subjectInfo->pClientData) return APPX_E_INVALID_SIP_CLIENT_DATA;
after this called
HRESULT Appx::Packaging::Packaging::SignFile(
PCWSTR FileName, APPX_SIP_CLIENT_DATA* sipClientData)
here at begin next code:
if (!sipClientData->pSignerParams) return APPX_E_INVALID_SIP_CLIENT_DATA;
this clear stated in msdn:
You must provide a pointer to an APPX_SIP_CLIENT_DATA structure as
the pSipData parameter when you sign an app package. You must
populate the pSignerParams member of APPX_SIP_CLIENT_DATA with
the same parameters that you use to sign the app package. To do this,
define your desired parameters on the SIGNER_SIGN_EX2_PARAMS
structure, assign the address of this structure to pSignerParams,
and then directly reference the structure's members as well when you
call SignerSignEx2.
question - why need again provide the same parameters, which used in call SignerSignEx2 ? because appxbundle is really archive, which containing multiple files. and every file need be sign. for this Appx::Packaging::Packaging::SignFile recursive call SignerSignEx2 again :
for this recursive calls pSignerParams and used - for call SignerSignEx2 with exactly the same parameters as top call

Qmake Command to copy directory and files

I want to copy the contents in directory "temp" to "dd"
BINARY_DESTINATION_PATH = $$PWD$$SEPARATOR/dd/
RESOURCE_SOURCE_PATH = $$PWD$$SEPARATOR/temp
EXPORTED_DESTINATION_PATH = $${BINARY_DESTINATION_PATH}
EXPORTED_DESTINATION_PATH ~= s,/,\\,g
EXPORTED_SOURCE_PATH = $${RESOURCE_SOURCE_PATH}
EXPORTED_SOURCE_PATH ~= s,/,\\,g
QT += core
QT -= gui
CONFIG += c++11
TARGET = sample
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
win32 {
QMAKE_POST_LINK += $$quote(cmd /c xcopy /S /I /Y $${EXPORTED_HEADERS}\\copy_to_output $${EXPORTED_HEADERS_WIN})
}
but this is not adding the files into dd and showing error on build.
Error i son line "QMAKE_POST_LINK"
quote(string)
Converts a whole string into a single entity and returns the result. This is just a fancy way of enclosing the string into double quotes.
What you're probably looking for is system(command).

calling gccxml from my code (Windows)

I'm writing a tool that makes use of gccxml. Basically I'm parsing the output xml file that has been created by gccxml. This works great on my windows machine in visual studio except for a couple of drawbacks. Here's the current state of my project:
cmake_gui gave me a visual studio solution that compiles perfectly (x64 Release). It's set up to create three executables in E:\cmake_builds\GCCXML\bin\Release.
My own C++ tool is located in a different VS solution file. When it's supposed to make use of gccxml the following code is used:
bool Parser::ParseFile( const std::string& _szFileName, std::string& _gccxmlPath,
const std::string& _tempFileLocation,
std::string& _errorStr)
{
bool retVal = true;
printf("Parsing file %s...\n\n", _szFileName.c_str());
/* format _gccxmlPath, adding a final forward slash to the path if required */
char lastChar = _gccxmlPath.at(_gccxmlPath.length()-1);
if(lastChar != '/' && lastChar != '\\')
_gccxmlPath += "/";
/* set up a temporary environment path variable so that the gccxml exe files may locate each other */
char envPath[500];
sprintf_s(envPath, "PATH=%s", _gccxmlPath.c_str());
const char* gccxml_env[] =
{
/* set path to gccxml directory where all exe files from gccxml are located */
envPath,
0
};
/* path & filename of gccxml.exe */
char gccxml_exe[500];
sprintf_s(gccxml_exe, "%sgccxml.exe", _gccxmlPath.c_str());
/* parameter string used to set gccxml output filename */
char fxmlParam[500];
sprintf_s(fxmlParam, "-fxml=\"%s\"", _tempFileLocation.c_str());
/* synthesize argument list for gccxml*/
/* see: http://gccxml.github.io/HTML/Running.html */
/* and: https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/Invoking-GCC.html */
const char* gccxml_args[GCCXML_PARAM_LEN];
unsigned int curPos = 0;
/* 1st argument: exe name */
gccxml_args[curPos++] = "gccxml.exe";
/* the source code to be compiled */
gccxml_args[curPos++] = _szFileName.c_str();
/* try to find out which msvc compiler to use */
gccxml_args[curPos++] = "--gccxml-compiler cl";
/* the output xml file */
gccxml_args[curPos++] = fxmlParam;
/* last argument: zero termination */
gccxml_args[curPos++] = 0;
/* call gccxml & compile the source code file */
if(0 != _spawnvpe(P_WAIT, gccxml_exe, gccxml_args, gccxml_env))
{
_errorStr += "GCCXML Compiler Error";
return false;
}
/* now parse the gccxml output file from tempfile ... */
...
...
return retVal;
}
as you can see I have to set up a local environment PATH variable to make sure the three executables are able to find each other.
This works great for what I want to do.
Unfortunately I can't use this method to call gccxml.exe when I move the three executables to a different directory. Of course I provide the new _gccxmlPath string but gccxml returns
"Support item Vc10/Include is not available..."
telling me that it looked in the folder into which I moved the executables. All my local copies of Vc10/Include however are located somewhere totally different and I don't understand how it had been able find one of these before I moved the executables.
It seems like this problem can be fixed by calling gccxml_vcconfig.exe using the parameters "patch_dir" and providing the directory "gccxml/Source/GCC_XML/VcInstall" from my gccxml source files. I'm, however, not able to solve my issue this way using any of the spawn* commands.
If I do the gccxml_vcconfig.exe runs just fine but after that I'm trying to call gccxml.exe and it turns out that it still looks in the same directory as before.
So gccxml_vcconfig.exe was probably not what I was looking for?
I'm trying to find a way to provide my tool to users who don't want to recompile gccxml on their machine so I'd like to distribute the thre binaries (and what else is needed).
just to let you know. I found a way of doing what I wanted to do. The trick is as follows:
right before vpe-spawning gccxml using its own location as environment (as shown above) vp-spawn the gccxml_vcconfig.exe without providing any environment path variables. This may look like this
std::string VcInstallDir = resolveRelativePath(_gccxmlPath + "../share/gccxml-0.9/VcInstall");
std::string GCCXML09Dir = resolveRelativePath(_gccxmlPath + "../share/gccxml-0.9");
std::vector<const char*> gccxml_config_args;
gccxml_config_args.push_back("gccxml_vcconfig.exe");
gccxml_config_args.push_back(VcInstallDir.c_str());
gccxml_config_args.push_back(GCCXML09Dir.c_str());
gccxml_config_args.push_back(0);
if(0 != _spawnvp(_P_WAIT, gccxml_vcconfig_exe.c_str(), gccxml_config_args.data()))
{
_errorStr += "GCCXML Configuration Error";
return false;
}
note that resolveRelativePath is a self written function for string manipulation that produces a valid absolute path; gccxml_vcconfig_exe contains the absolute path to my exe file
and I somewhat changed my coding style from arrays to std::vectors as you can see

Prevent console window from being created in custom node.js build

I'm creating a custom build of node.js that should not show a console window to the user.
I've tried changing the linker config in the gyp file to 2 (which should set the linker flag /SUBSYSTEM:WINDOWS), but I still get a console window when I run the resulting node.exe binary.
How can I prevent the console window from appearing?
Edit: Further investigation reveals that the linker config in node.gyp is not taking effect. The generated node.vcxproj still has <Link><SubSystem>Console</SubSystem></Link> (which is very strange to me, since adding 'UACUIAccess': 'true' in the same part of node.gyp did take effect), so the built binary is incorrectly linked.
Solution 1
Save this one line of text as file invisible.vbs:
CreateObject(“Wscript.Shell”).Run “”"” & WScript.Arguments(0) & “”"”, 0, False
To run any program or batch file invisibly, use it like this:
wscript.exe “C:\Wherever\invisible.vbs” “C:\Some Other Place\MyBatchFile.bat”
To also be able to pass-on/relay a list of arguments use only two double quotes
CreateObject(“Wscript.Shell”).Run “” & WScript.Arguments(0) & “”, 0, False
eg: Invisible.vbs “Kill.vbs ME.exe”
Solution 2
Use a command line tool to silently launch a process : Quiet.
Solution 3
Roll your own C++ Win32 App:
PROCESS_INFORMATION procInfo = {0};
STARTUPINFOstartupInfo = {0};
SECURITY_ATTRIBUTESsaAttr = {0};
HANDLEhStdIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLEhStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLEhStdErr = GetStdHandle(STD_ERROR_HANDLE);
// build up security attributes
saAttr.nLength = sizeof(saAttr);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// set file handles for process to be created
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdInput = hStdIn;
startupInfo.hStdOutput = hStdOut;
startupInfo.hStdError = hStdErr;
// build command line: format is [cmd.exe /c "%batchScript%" %batchArgs%]
if (-1 == _snprintf_s(cmd, sizeof(cmd),"cmd.exe /c \"%s\" %s", batchScript, batchArgs))
errorExit("_snprintf_s(\"cmd.exe /c \"%%s\" %%s\"), \"%s\", \"%s\") failed.", batchScript, batchArgs);
rc = CreateProcess(NULL, cmd, NULL, &saAttr, TRUE, CREATE_NO_WINDOW, NULL, tempPath, &startupInfo, &procInfo);
You have to change the SubSystem field value in node.exe PE optional header. The current value is 3 which is defined as Windows Console. If you change it to 2 (which is defined as Windows GUI) there would be no console window. In order to patch the executable file, you have to use utilities to change Optional Header of PE.
One example of such a tool is PE tools.
Click on Optinal Header and then change the Subsystem from 3 to 2.
That`s all.
Remember that with this change you can only run js files. You can not use interactive mode.
It appears that you must:
Comment out the 'SubSystem': 1 line in common.gypi. (Changing it to 2 causes the build to fail in mksnapshot.)
Change SubSystem to 2 in node.gyp
Also add 'EntryPointSymbol': 'wmainCRTStartup' to node.gyp.
This builds a node.exe that does not create a console window.

Resources