I have the following code (extract) to display Save As dialog:
char FileName[MAX_PATH] = "TestImage.jpg"
...
lpofn.lStructSize = sizeof(OPENFILENAME);
lpofn.hwndOwner = hWnd;
lpofn.hInstance = GetWindowInstance (hWnd);
lpofn.lpstrFilter = "JPG - JPEG File\0*.JPG\0TIF - TIFF File\0*.TIF\0PNG File\0*.PNG\0BMP - Bitmat File\0*.BMP\0";
lpofn.lpstrCustomFilter = NULL;
lpofn.nMaxCustFilter = NULL;
lpofn.nFilterIndex = 0;
lpofn.lpstrFile = FileName;
lpofn.nMaxFile = MAX_PATH;
lpofn.lpstrFileTitle = NULL;
lpofn.nMaxFileTitle = NULL;
lpofn.lpstrInitialDir = NULL;
lpofn.lpstrTitle = NULL;
lpofn.Flags = OFN_HIDEREADONLY | OFN_ENABLEHOOK | OFN_EXPLORER;
lpofn.nFileOffset = 0;
lpofn.nFileExtension = 0;
lpofn.lpstrDefExt = NULL;
lpofn.lCustData = NULL;
lpofn.lpfnHook = &UpdateFilename;
lpofn.lpTemplateName = NULL;
if(!GetSaveFileName(&lpofn)) return;
...
eg.
- User does save as, default File name = "TestImage.jpg", default Files of Type = JPG
- User changes Files of Type to PNG, File name control remains at "TestImage.jpg" instead of changing to "TestImage.png"
Am I doing something wrong? Is it possible to instruct GetSaveFileName() to change the extension, or do I have to have a custom save as dialog (any examples?)
I'm using Win32 API, VC6.
Update: here is the Hook function:
UINT CALLBACK UpdateFilename(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg)
{
case WM_NOTIFY:
// Check for CDN_TYPECHANGE etc
return FALSE;
}
return FALSE;
}
Note that the hook function does stop on breakpoints. I've purposely not proceeded further with handling CDN_TYPECHANGE until I can figure out why the look of the dialog changes when the hook is enabled and how to fix it.
Use a buffer for your suggested file name, not a static string.
char szFile[MAX_PATH];
szFile[0] = '\0';
lpofn.lpstrFile= szFile;
lpofn.nMaxFile = sizeof(szFile)/ sizeof(*szFile);
Besides that, it is a default Windows behaviour going well back to Win95 days. As you are using VS6.0, you stil have these DLLs. What I did then was to use:
lpofn.lpstrDefExt = (LPSTR)NULL
That prevents any extension from being added. I then checked lpofn.nFileExtension upon return to find out which extension was selected.
if (lpofn.nFileExtension == 0)
{
// add default extension, no extension was selected/entered by user
}
else
{
// there is an extension, save as entered.
}
In order to update the dialog while it is still running, you need to provide a pointer to an lpfnHook callback in the OPENFILENAME struct, and have the callback handle the CDN_TYPECHANGE notification. It can send the dialog a CDM_GETFILEPATH or CDM_GETSPEC message to get the current filename, tweak it as needed, and then send a CDM_SETCONTROLTEXT message to update the edit field (the ID of the filename edit field is 0x442) with the new value.
Update: There is nothing wrong with your hook code. GetSaveFileName() is deprecated starting in Windows Vista, replaced by (and becoming a wrapper around) the Common Item Dialog. The GSFN dialog UI is not altered by a hook in XP, so you must be using Vista+, in which case enabling the hook simply causes the wrapper to use different settings when invoking the CID internally. A lot of the new CID features are based on IShellItem, not filename strings, so the wrapper removes anything that cannot be represented as a old-style filename, and makes the dialog look like the old-style GSFN dialog in XP and earlier. So what you are seeing is normal behavior for GetSaveFileName() under Vista+! If you do not like it, then do not use GetSaveFileName() anymore. Use the new IFileSaveDialog interface instead. In fact, it natively changes the file extension for you if you configure multiple file types, designate one of them as the default extension, and then set an initial filename that matches the default extension. But if you wanted to, you can alternatively implement the IFileDialogEvents interface in your code to receive OnTypeChange notifications and then use the IFileDialog::SetFileName() method to update the displayed filename as neded.
Related
I tried to get a script to create a text file that could write/add the images name, but the function
FileID = CreateFileForWriting(filename) does not work, it shows that was used by other process
I did not get this, is this function not right format or something is wrong, thx
Number Totaln
totaln=countdocumentwindowsoftype(5)
String filename, text
Number fileID
if (!SaveasDialog( "save text file as",getapplicationdirectory(2,0) + "Imagename.txt", filename))exit(0)
fileID = CreateFileForWriting(filename)
number i
for(i = 0; i <totaln; i++)
{
image imgSRC
imgSRC := GetFrontImage()
string imgname=getname(imgSRC)
WriteFile(fileID,"imgname")
Result("imgname")
}
Your code is nearly fine, but if you use the low-level API for file I/O you need to ensure that you close files you've opened or created.
Your script doesn't. Therefore, it runs fine exactly 1 time but will fail on re-run (when the file is still considered open.)
To fix it, you need to have closefile(fileID) at the end.
( BTW, if you script exits or throws after opening a file but before closing it, you have the same problem. )
However, I would strongly recommend not using the low-level API but the file streaming object instead. It also provides an automated file-closing mechanism so that you don't run into this issue.
Doing what you do in your script would be written as:
void writeCurrentImageNamesToText()
{
number nDoc = CountImageDocuments()
string filename
if (!SaveasDialog( "save text file as",getapplicationdirectory(2,0) + "Imagename.txt", filename)) return
number fileID = CreateFileForWriting(filename)
object fStream = NewStreamFromFileReference(fileID,1) // 1 for auto-close file when out of scope
for( number i = 0; i <nDoc; i++ ){
string name = GetImageDocument(i).ImageDocumentGetName()
fStream.StreamWriteAsText( 0, name + "\n" ) // 0 = use system encoding for text
}
}
writeCurrentImageNamesToText()
Using the Calendar component in Extjs 7.0 we noticed that the header cells didn't line up correctly with their columns if the language was set to Dutch:
When checking the source code I found the place where these values are added in the cell html;
In Ext.calendar.header.Base, in the setHeaderText function the following code exists:
var me = this,
D = Ext.Date,
value = me.getValue(),
format = me.getFormat(),
domFormat = me.domFormat,
cells = me.cells,
len = cells.length,
useDates = me.useDates,
cell, i;
if (!value) {
return;
}
value = D.clone(value);
for (i = 0; i < len; ++i) {
cell = cells[i];
if (useDates) {
cell.setAttribute('data-date', D.format(value, domFormat));
}
cell.setAttribute('data-day', value.getDay());
cell.innerHTML = D.format(value, format);
value = D.add(value, D.DAY, 1);
}
The innerHtml is set by formatting the Date(D) object which results in the 3 characters of that day. If you change this to just setting a 4 char value like cell.innerHTML = 'Test' the headers line up just fine:
But for some reason this doesn't work when using the D.format value. If somebody has any idea what causes this, I would love to hear.
I can't seem to test if this also goes wrong in another language cause for some reason my packages can't be loaded in anymore.
You can set(or fix) localization override localization constants.
Calendar use Ext.Date.dayNames and Ext.Date.getShortDayName().
All constants list you can see in localization package.
Fiddle example
I'm writing a WebExtension for Firefox. In it, I want to be able to copy arbitrary text between the webextension and the clipboard. As far as I could see from the documentation (https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard), there is no way to transfer data between a javascript variable and the clipboard, it seems I need to take a detour through a DOM element.
For copying text into the clipboard, I've come up with a dummy method, which is very similar to what is described in this question: Firefox webextension not copying to clipboard
function copy(contentToCopy) {
var txtToCopy = document.createElement('input');
txtToCopy.style.left = '-300px';
txtToCopy.style.position = 'absolute';
txtToCopy.value = contentToCopy;
document.body.appendChild(txtToCopy);
txtToCopy.select();
console.log("Copying ", txtToCopy.value);
var res = document.execCommand('copy');
console.log("Copy result ", res);
txtToCopy.parentNode.removeChild(txtToCopy);
}
I can then call it with
copy('any arbitrary text');
and it works perfectly.
However, I also need to access the clipboard contents in the same way, and can't get it to work:
function paste() {
var txtToPaste = document.createElement('input');
txtToPaste.style.left = '-300px';
txtToPaste.style.position = 'absolute';
txtToPaste.value = 'dummy content for debugging';
document.body.appendChild(txtToPaste);
txtToPaste.focus();
txtToPaste.select();
var res = document.execCommand('paste');
var result = txtToPaste.value;
console.log("Paste result ", res);
console.log('Pasted text', result);
console.log('txtToPaste', txtToPaste);
txtToPaste.parentNode.removeChild(txtToPaste);
return result;
}
I have also requested the appropriate permission in my manifest.json file:
"permissions": ["clipboardRead" ]
I then try to call the method like this:
var dataFromClipboard = paste();
However, no matter what data I have in my clipboard when I call the method, the "Paste result" is always "true" and "result" is "dummy content for debugging" (i.e. unchanged from what I used to initialise the dummy field).
I'm testing this with Firefox 57.0.2 (64-bit) on Windows 7 (64-bit).
Am I missing something obvious? Why does this work in one direction but not the other?
The javascript console (neither in the tab in which the extension is being tested nor in the global browser console) is not showing any errors.
After another look at https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard#Reading_from_the_clipboard I saw the section titled "Browser-specific considerations". I don't know why I missed it on my first read through, but it provides the solution:
Firefox supports the "clipboardRead" permission from version 54, but
does require an element in content editable mode, which for content
scripts only works with a <textarea>.
So with this knowledge I have modified my function as follows:
function paste() {
var txtToPaste = document.createElement('textarea');
txtToPaste.id = "txtToPaste";
txtToPaste.style.left = '-300px';
txtToPaste.style.position = 'absolute';
txtToPaste.contentEditable = true;
txtToPaste.textContent = '';
document.body.appendChild(txtToPaste);
txtToPaste.focus();
txtToPaste.select();
var res = document.execCommand('paste');
var result = txtToPaste.textContent;
console.log("Copy result ", res);
console.log('Pasted text', result);
console.log('txtToPaste', txtToPaste);
txtToPaste.parentNode.removeChild(txtToPaste);
return result;
}
With these changes (changing input to textarea; setting contentEditable to true) the method works as I had hoped.
Using Windows Defender API , I'm trying to do a scan for malwares on a folder.
Following The documentation I wrote the code:
MPRESOURCE_INFO ResourceInfo = { 0 };
MPSCAN_RESOURCES ScanResource = { 0 };
PMPRESOURCE_INFO ResourceInfoArray = NULL;
...
ResourceInfo.Scheme = L"dir";
ResourceInfo.Path = L"C:\\temp";
ResourceInfo.Class = 0;
// ResourceInfoArray was Allocated before
*ResourceInfoArray = ResourceInfo;
ScanResource.dwResourceCount = 1;
ScanResource.pResourceList = ResourceInfoArray;
// Opened hMpManager before using MpScanStart
hRetval = MpScanStart(hMpManager, MPSCAN_TYPE_RESOURCE, 0, &ScanResource, NULL, &ScanHnadle);
From which I get an error message: An unexpected problem occurred. Install any available updates, and then try to start the program again. For information on installing updates, see Help and Support.
However If I change the ResourceInfo definition to:
ResourceInfo.Scheme = L"file";
ResourceInfo.Path = L"C:\\temp\\MyFile.exe";
ResourceInfo.Class = 0;
It works great, detecting the file in the right way.
On the bottom line - the code works for files, but doesn't work for directories.
Does anyone know what am I doing wrong with the directory search?
Analyzing event logs created by MpCmdRun.exe I found out that it uses the scheme "folder" instead of "dir". That change made my code working.
ResourceInfo.Scheme = L"folder";
Folder paths do not have to end with backslash, but drives require it: (F:\).
I am looking for a Win32 API call to return the runtime context of my process. I want to be able to programmatically test if I am running as a service or am I running as standard application process.
Several ideas come to mind.... Since I always have service DAD.exe who runs SON.exe sometimes as his child and in service context --- and sometimes SON.exe is started not by DAD, and by a user.
SON.EXE would do API whoami() to learn which context he is running in.
Now DAD could create an environment var -- and then SON could test for this var -- and if found he knows he is a son of DAD and thus runnning as a service..... But this is weak...
Another idea would be to look at my SID or token and see if I could make this determination.... Again this looks at best more complex vs. a single API check...
The simple low-tech solution to this is to register your service to run with command line arguments that identify it as a service.
Another option is to use the Tool Help library. Using it, you take a snapshot of all the currently running processes and then you can walk through all the processes using the Process32First and Process32Next function. These return a structure (PROCESSENTRY32) that looks like:
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
} PROCESSENTRY32, *PPROCESSENTRY32;
as you walk through all the processes, as soon as you find the one whose th32ProcessID matches the one for SON.exe (see GetCurrentProcessId or GetProcessId ). If the th32ParentProcessID of that structure matches that of DAD.exe, then you know you were launched from DAD.exe.
Edit:
Answering your comment, I guess you could go one step further and then see who the parent of DAD.exe is, if it's services.exe, then you're a service.
Reading the documentation, I think you could determine whether you're in an interactive session or service via:
GetProcessWindowStation
GetUserObjectInformation(UOI_FLAGS)
and then WSF_VISIBLE should tell you.
If you wanted to differentiate between a logged-in user session and one that's inactive (Fast User Switching), I guess you could use GetThreadDesktop and GetUserObjectInformation(UOI_IO).
The best and simplest way to tell from inside the service is to set a flag when ServiceMain is called. But you're testing a child process, so see above.
I found the following:
bool WinUtil::IsServiceUser(HANDLE hToken, bool *is_service) {
if (is_service == NULL) {
return false;
}
TOKEN_STATISTICS ts;
DWORD dwSize = 0;
// Use token logon LUID instead of user SID, for brevity and safety
if (!::GetTokenInformation(hToken, TokenStatistics,
(LPVOID)&ts, sizeof(ts), &dwSize)) {
return false;
}
// Compare LUID
const LUID SystemLuid = SYSTEM_LUID;
const LUID LocalServiceLuid = LOCALSERVICE_LUID;
const LUID NetworkServiceLuid = NETWORKSERVICE_LUID;
if (EqualLuid(SystemLuid, ts.AuthenticationId) ||
EqualLuid(LocalServiceLuid, ts.AuthenticationId) ||
EqualLuid(NetworkServiceLuid, ts.AuthenticationId)) {
*is_service = true;
return true;
}
// Not a service account
*is_service = false;
return true;
}
bool WinUtil::IsServiceProcess(bool *is_service) {
if (is_service == NULL) {
return false;
}
if (Util::IsVistaOrLater()) {
// Session 0 is dedicated to services
DWORD dwSessionId = 0;
if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &dwSessionId) ||
(dwSessionId == 0)) {
*is_service = true;
return true;
}
}
// Get process token
HANDLE hProcessToken = NULL;
if (!::OpenProcessToken(::GetCurrentProcess(),
TOKEN_QUERY | TOKEN_QUERY_SOURCE,
&hProcessToken)) {
return false;
}
ScopedHandle process_token(hProcessToken);
// Process token is one for a service account.
if (!IsServiceUser(process_token.get(), is_service)) {
return false;
}
return true;
}
I think your looking for Topshelf http://topshelf-project.com/, it does the heavy lifting and makes it easier run as console or install as a service. Topshelf hosting application debugging in VS2010