Using IPersistFile::Load multiple times with IPropertyStore - winapi

I am iterating through a bunch of .lnk files and getting information on them.
For each file I do this, this is js-ctypes but is a winapi question. I removed the error checking and simplified the code:
var hr_CoInitializeEx = ostypes.API('CoInitializeEx')(null, ostypes.CONST.COINIT_APARTMENTTHREADED);
var shellLinkPtr = ostypes.TYPE.IShellLinkW.ptr();
var hr_CoCreateInstance = ostypes.API('CoCreateInstance')(ostypes.CONST.CLSID_ShellLink.address(), null, ostypes.CONST.CLSCTX_INPROC_SERVER, ostypes.CONST.IID_IShellLink.address(), shellLinkPtr.address());
shellLink = shellLinkPtr.contents.lpVtbl.contents;
var persistFilePtr = ostypes.TYPE.IPersistFile.ptr();
var hr_shellLinkQI = shellLink.QueryInterface(ostypes.CONST.IID_IPersistFile.address(), persistFilePtr.address());
persistFile = persistFilePtr.contents.lpVtbl.contents;
var propertyStorePtr = ostypes.TYPE.IPropertyStore.ptr();
var hr_shellLinkQI2 = shellLink.QueryInterface(ostypes.CONST.IID_IPropertyStore.address(), propertyStorePtr.address());
propertyStore = propertyStorePtr.contents.lpVtbl.contents;
for (var i = 0; i < arrOSPath.length; i++) {
var hr_Load = persistFile.Load(persistFilePtr, arrOSPath[i], 0);
var ppropvar = ostypes.TYPE.PROPVARIANT();
var hr_GetValue = propertyStore.GetValue(ostypes.CONST.PKEY_AppUserModel_ID.address(), ppropvar.address());
console.log(ppropvar.pwszVal); ////// <<<<< this is fishy
var rez_PropVariantClear = ostypes.API('PropVariantClear')(ppropvar.address());
}
// cleanup
persistFile.Release(persistFilePtr);
propertyStore.Release(propertyStorePtr);
shellLink.Release(shellLinkPtr);
ostypes.API('CoUninitialize')();
However ppropvar.pwszVal is coming out to be the SystemAppUserModelID of the first file I IPersitFile::Loaded, is this expected? Do I have to CoUninitialize and CoInitialize each loop or something to fix this?
Thanks

Related

cannot read property "0" of undefined error

I have a code that pulls information from all excel type files in some folders on google drive. The problem is that there are over 100 files and the code only pulls data from around 30 files and shows the following error: "TypeError: Cannot read property '0' of undefined" The error is in line 12, "console.log(lista_carpetas_ok2[i][0]) // Here, you can see the folder ID in the log."
function listfechas() {
var ss2 = SpreadsheetApp.getActiveSpreadsheet();
var carpetasSheet = ss2.getSheetByName("carpetas");
var lista_carpetas = carpetasSheet.getRange("C2:C" + carpetasSheet.getLastRow()).getValues();
var lista_carpetas_ok2 = lista_carpetas.filter(([a]) => a);
var sheet2 = ss2.getSheetByName("SS23");
sheet2.clear();
sheet2.appendRow(["Folder", "Name", "SMS","rec SMS", "FIT", "rec FIT", "2FIT", "rec 2FIT" ,"3FIT" ,"rec 3FIT" ,"PP" ,"rec PP" ,"2PP" ,"rec 2PP", "3PP", "rec 3PP","SHIP", "rec SHIP", "2SHIP","rec 2SHIP"]);
for (var i = 0; i < 5; i++) {
console.log(lista_carpetas_ok2[i][0]) // Here, you can see the folder ID in the log.
var folderid = lista_carpetas_ok2[i][0];
try {
var parentFolder =DriveApp.getFolderById(folderid);
listFiles(parentFolder,parentFolder.getName())
listSubFolders(parentFolder,parentFolder.getName());
} catch (e) {
Logger.log(e.toString());
}
}
function listSubFolders(parentFolder,parent) {
var childFolders = parentFolder.getFolders();
while (childFolders.hasNext()) {
var childFolder = childFolders.next();
Logger.log("Fold : " + childFolder.getName());
listFiles(childFolder,parent)
listSubFolders(childFolder,parent + "|" + childFolder.getName());
}
}
function listFiles(fold,parent){
var data = [];
var files = fold.getFilesByType(MimeType.GOOGLE_SHEETS);
try {
while (files.hasNext()) {
var file = files.next();
var sms = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("A2").getValue();
var recsms = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("B2").getValue();
var fit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("C2").getValue();
var recfit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("D2").getValue();
var dfit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("E2").getValue();
var recdfit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("F2").getValue();
var tfit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("G2").getValue();
var rectfit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("H2").getValue();
var pp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("I2").getValue();
var recpp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("J2").getValue();
var dpp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("K2").getValue();
var recdpp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("L2").getValue();
var tpp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("M2").getValue();
var rectpp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("N2").getValue();
var ship = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("O2").getValue();
var recship = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("P2").getValue();
var dship = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("Q2").getValue();
var recdship = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("R2").getValue();
var fullRange = sheet2.getRange("A1:Z1001");
fullRange.setVerticalAlignment(DocumentApp.VerticalAlignment.TOP);
data = [
fold.getName(),
file.getName(),
sms,
recsms,
fit,
recfit,
dfit,
recdfit,
tfit,
rectfit,
pp,
recpp,
dpp,
recdpp,
tpp,
rectpp,
ship,
recship,
dship,
recdship,
];
sheet2.appendRow(data);
};
} catch (e) {
// In this modification, when your folder ID cannot be used, that folder ID is skipped. At that time, an error message can be seen in the log.
console.log(e.message);
}
}
}
When I wrote the code, I added catch (e) which I thought skipped the folder if there was no excel file in that folder, but the code stops at folder "QM0201" which does not have an excel file in it.
Does anybody know how I can fix it? I would like the code to run even if some folders don't have an excel files in them, those should just be skipped.
Thank you so much in advance! Any help is appreciated.
To avoid the error, replace
for (var i = 0; i < 5; i++) {
by
for (var i = 0; i < lista_carpetas_ok2.length; i++) {
This might also help to avoid the code to stop at certain folder.
Regarding the error
> Info Cannot read property 'getRange' of null"
This occurs because the spreadsheet hasn't a sheet named fechas
to avoid this error you might add
if(!SpreadsheetApp.open(file).getSheetByName("fechas")) break;
above of
var sms = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("A2").getValue();
if(
Debugging tips:
To verify that the correct values are assigned to lista_carpetas_ok2, add console.log(JSON.stringify(lista_carpetas_ok2)); just below lista_carpetas_ok2 declaration so you can review the values assigned to the variable.
To have more informative logs when an error occurs, instead of
} catch (e) {
Logger.log(e.toString()); // or console.log(e.message);
}
use
} catch (e) {
console.log(e.message, e.stack);
}
This is hardly a cause of the error but who knows, the code has the very unefficient part — 72 calls to the server. It can be greatly improved if you change this:
var sms = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("A2").getValue();
var recsms = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("B2").getValue();
var fit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("C2").getValue();
var recfit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("D2").getValue();
var dfit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("E2").getValue();
var recdfit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("F2").getValue();
var tfit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("G2").getValue();
var rectfit = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("H2").getValue();
var pp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("I2").getValue();
var recpp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("J2").getValue();
var dpp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("K2").getValue();
var recdpp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("L2").getValue();
var tpp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("M2").getValue();
var rectpp = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("N2").getValue();
var ship = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("O2").getValue();
var recship = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("P2").getValue();
var dship = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("Q2").getValue();
var recdship = SpreadsheetApp.open(file).getSheetByName("fechas").getRange("R2").getValue();
To this:
var values = SpreadsheetApp.open(file).getSheetByName("fechas").getRange('A2:R2').getValues().flat();
var [sms,recsms,fit,recfit,dfit,recdfit,tfit,rectfit,pp,recpp,dpp,recdpp,tpp,rectpp,ship,recship,dship,recdship] = values;
It will reduce the quantity of calls to 4!
It should work faster and, if the real cause of the error is exceed of time limit (~6 min) this improvement can help. Try it.
Probably you could simplify the code further. Instead of this:
data = [
fold.getName(),
file.getName(),
sms,
recsms,
fit,
recfit,
dfit,
recdfit,
tfit,
rectfit,
pp,
recpp,
dpp,
recdpp,
tpp,
rectpp,
ship,
recship,
dship,
recdship,
];
You can use this:
var values = SpreadsheetApp.open(file).getSheetByName("fechas").getRange('A2:R2').getValues().flat();
var data = [fold.getName(), file.getName(), ...values];
No need to use the 18 variables, as far as I can see. It barely affects on the speed, though.

how to to convert for to foreach

jslint tell Unexpected 'for'.
so i think that i must convert for with foreach
but how?
if someone can help
thanks
// Grab the original element
var original = document.getElementsByTagName("noscript")[0];
// Create a replacement tag of the desired type
var replacement = document.createElement("span");
var i;
// Grab all of the original's attributes, and pass them to the replacement
for(i = 0, l = original.attributes.length; i < l; ++i){
var nodeName = original.attributes.item(i).nodeName;
var nodeValue = original.attributes.item(i).nodeValue;
replacement.setAttribute(nodeName, nodeValue);
}
// Persist contents
replacement.innerHTML = original.innerHTML;
// Switch!
original.parentNode.replaceChild(replacement, original);
You have a comma after i = 0, <========
it should be semicolon.
Another issue is declaring l = original.attributes.length you don't need the variable l
just use it as for(i = 0; i < original.attributes.length; ++i){
if you still wanna use a forEach you can do it as:
original.attributes.forEach(element => {
var nodeName = element.nodeName;
var nodeValue = element.nodeValue;
replacement.setAttribute(nodeName, nodeValue);
});
thanks for your answer, i got Uncaught TypeError: original.attributes.forEach is not a function
function Switch() {
var original = document.getElementsByTagName("noscript")[0];
var replacement = document.createElement("span");
original.attributes.forEach(element => {
var nodeName = element.nodeName;
var nodeValue = element.nodeValue;
replacement.setAttribute(nodeName, nodeValue);
});
// Persist contents
replacement.innerHTML = original.innerHTML;
// Switch!
original.parentNode.replaceChild(replacement, original);
}

Is there a way to speed up this for loop in Google Script?

I have a for loop which is working on my google sheet but it takes around 5 minutes to filter through the 2100 rows of data. I have read about using filters and getting rid of the for loop all together but I'm fairly new to coding in Google Script and haven't been able to get my head around the syntax for this. Any advice greatly appreciated.
Code below:
function Inspect() {a
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = sSheet.getSheetByName("Inventory");
var tarSheet = sSheet.getSheetByName("Inspections");
var lastRow = srcSheet.getLastRow();
for (var i = 2; i <= lastRow; i++) {
var cell = srcSheet.getRange("A" + i);
var val = cell.getValue();
if (val == true) {
var srcRange = srcSheet.getRange("B" + i + ":I" + i);
var clrRange = srcSheet.getRange("A" + i);
var tarRow = tarSheet.getLastRow();
tarSheet.insertRowAfter(tarRow);
var tarRange = tarSheet.getRange("A" + (tarRow+1) + ":H" + (tarRow+1));
var now = new Date();
var timeRange = tarSheet.getRange("I"+(tarRow+1));
timeRange.setValue(now);
srcRange.copyTo(tarRange);
clrRange.clear();
//tarRange.activate();
timeRange.offset(0, 1).activate();
}
}
};
Yes, to speed-up you will need to get all the values first and apply your logic to the obtained 2D-arrays instead of cells, at the end you will use setValues to update your sheet. I would go for something like this:
function Inspect() {
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = sSheet.getSheetByName("Inventory");
var tarSheet = sSheet.getSheetByName("Inspections");
var srcLastRow = srcSheet.getLastRow();
var tarLastRow = tarSheet.getLastRow();
var srcArray = srcSheet.getRange(1,1,srcLastRow,9).getValues();//(A1:I(lastrow))
var tarArray = tarSheet.getRange(1,1,tarLastRow,9).getValues();//(A1:I(lastrow))
for (var i = 1; i < srcArray.length; i++) {
var val = srcArray[i][0];
if (val == true) {
var copyValues = srcArray[i].slice(1);//Get all elements from the row excluding first column (srcSheet.getRange("B" + i + ":I" + i);)
var now = new Date();
copyValues[8]=now;//set the time on column 9 (array starts at position 0!)
var tarNewLine = copyValues;
tarArray.push(tarNewLine);
//clear values on source (except column A):
for(var j=1;j<srcArray[i].length;j++){
srcArray[i][j]="";
}
}
}
tarSheet.clear();
tarSheet.getRange(1, 1,tarArray.length,tarArray[0].length).setValues(tarArray);
srcSheet.clear();
srcSheet.getRange(1, 1,srcArray.length,srcArray[0].length).setValues(srcArray);
};
You cannot get around a loop, but you should reduce the number of calls to the SpreadsheetApp to a minimum, see Apps Script Best Practices
It is not the for loop, but those calls that make your code slow. Instead, work with arrays as much as you can. Loops become of a problem if they are nested - this is also something you should avoid.
Sample how to perform most calls to SpreadsheetApp outside of the loop and work with arrays:
function Inspect() {
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = sSheet.getSheetByName("Inventory");
var tarSheet = sSheet.getSheetByName("Inspections");
var lastRow = srcSheet.getLastRow();
var Acolumn = srcSheet.getRange("A2:A" + lastRow);
var Avalues = Acolumn.getValues();
var srcRange = srcSheet.getRange("B2:I" + lastRow);
var srcValues = srcRange.getValues();
var array = [];
var now = new Date();
for (var i = 0; i < lastRow-1; i++) {
var val = Avalues[i][0];
if (val == true) {
srcValues[i].push(now);
array.push(srcValues[i]);
var clrRange = Acolumn.getCell(i+1, 1);
clrRange.clear();
}
}
var tarRow = tarSheet.getLastRow();
tarSheet.insertRowAfter(tarRow);
if(array.length!=0){
var tarRange = tarSheet.getRange("A" + (tarRow+1) + ":I" + (tarRow + array.length));
tarRange.setValues(array);
}
};

How do i get this code to copy only the rows that match my object matching criteria? currently if copies every row

Currently this code copies every row and not the ones that match my criteria
I had some issues getting it to loop. Hence the loggers.
I'm quite new to this.
function searchAnotherSheetAndReturnRows(){
Logger.clear()
var tss = SpreadsheetApp.openById('1dX5MMLXGsG_S_KzRhOcquOp00VhzRBSlTCkyw'); // tss = target spreadsheet
var ts = tss.getSheetByName('Sheet1'); // ts = target sheet
var sss = SpreadsheetApp.openById('1ImDswr6ADS7t4HKIFHpntykN2LS7Mmd-cls34'); // sss = source spreadsheet
var ss = sss.getSheetByName('Page1'); // ss = source sheet
var tslast_row = ts.getLastRow();
var i=0;
Logger.log(i)
for (var i = 1; i < tslast_row; i++) {
Logger.log(i)
Logger.log(tslast_row);
var ssSearch1 = ss.getRange(2,1).getValue();
Logger.log(ssSearch1);
var tsSearch1 = ts.getRange(i,1).getValue();
Logger.log(tsSearch1);
if (ssSearch1 == tsSearch1); {
var tsSearch1Range = ts.getRange(i,1,1,7);
Logger.log(tsSearch1Range);
var range2Copy = tsSearch1Range.getValues();// Copies data from root sheet
Logger.log(range2Copy);
var last_row = ss.getLastRow();
Logger.log(last_row);
var last_row = last_row+1;
Logger.log(last_row);
ss.getRange(last_row,1,1,7).setValues(range2Copy);// Adds root sheet data to target
}
}
}
It's really hard to figure out what you're trying to do. So here's an example that will hopefully help you a little. The job of this function is to copy rows in the AlaskanWilderness that contain GOLD to MyPocket. (Names of Sheets)
function moveGoldFromAlaskanWildernessToMyPocket() {
var tss=SpreadsheetApp.openById('id');
var ts=tss.getSheetByName('MyPocket');
var sss=SpreadsheetApp.openById('id');
var ss=sss.getSheetByName('AlaskanWildernes');
var sr=ss.getDataRange()
var sd=sr.getValues();
var criteria='GOLD';
for (var i=1;i<sd.length;i++) {//skip header
for(var j=0;j<sd[i].length;i++) {
if (sd[i][j]=='GOLD'); {
ss.appendRow(sd[i]);
}
}
}
}
sheet.appendRow
range.getValues

Third Listbox populate from first 2 Listboxes e.parameter [GAS]

I'm having a similar issue to this question - however I am requiring to get 2 e.parameters of both 1st and 2nd list boxes in order to populate the spreadsheet for the 3rd list box.
The vert is being read by the e.parameter, but the chan is undefined, which I do not understand as it is fully functional in the second list box but not the third.
Is there something that I have overseen in this code?
var channelBox = app.createListBox().setId('channelBox').setName('channelBox').setWidth(152).setHeight(22);
var chanHandler = app.createServerChangeHandler('showChannelinfo');
chanHandler.addCallbackElement(channelBox);
channelBox.addChangeHandler(chanHandler);
var chaArray = ['Select channel','--','Email','Phone','Chat']; for (var i=0;i<chaArray.length;++i) {channelBox.addItem(chaArray[i]);};
var verticalBox = app.createListBox().setId('verticalBox').setName('verticalBox').setWidth(152).setHeight(22);
var vertHandler = app.createServerChangeHandler('showVerticalinfo');
vertHandler.addCallbackElement(verticalBox);
verticalBox.addChangeHandler(vertHandler);
function showChannelinfo(e){
var app = UiApp.getActiveApplication();
var itemSpreadsheetKey = 'ABC------------123';
var openedSS = SpreadsheetApp.openById(itemSpreadsheetKey);
var sheetList3 = openedSS.getSheetByName("boolean3");//Vertical Lookup Sheet
var channelBox = app.getElementById('channelBox');
var verticalBox = app.getElementById('verticalBox');
var chan = e.parameter.channelBox;
if (chan == 'Phone')
{ verticalBox.clear();
var marketFilter = sheetList3.getRange('A1').setFormula('=iferror(filter(boolean2!A:D,boolean2!B:B=TRUE))');}
if (chan == 'Email')
{ verticalBox.clear();
var marketFilter = sheetList3.getRange('A1').setFormula('=iferror(filter(boolean2!A:D,boolean2!D:D=TRUE))');}
if (chan == 'Chat')
{ verticalBox.clear();
var marketFilter = sheetList3.getRange('A1').setFormula('=iferror(filter(boolean2!A:D,boolean2!C:C=TRUE))');}
var numItemList1 = sheetList3.getLastRow();//get the item array
var list1ItemArray = sheetList3.getRange(1,1,numItemList1).getValues();//Add the items in ListBox
for(var i=0; i<list1ItemArray.length; i++)
{verticalBox.addItem(list1ItemArray[i][0]);}
if (chan == '--')
{verticalBox.clear();
var vertArray = ['Select vertical','Hardware','Apps','Ebooks','Movies','Music','Nik','Shopping','Places','Wallet','YouTube','BeatThatQuote'];
for (var i=0;i<vertArray.length;++i) {verticalBox.addItem(vertArray[i]);};}
return app
};
function showVerticalinfo(e){
var app = UiApp.getActiveApplication();
var itemSpreadsheetKey = 'ABC------------123';
var openedSS = SpreadsheetApp.openById(itemSpreadsheetKey);
var sheetList4 = openedSS.getSheetByName("boolean4");//Language Lookup Sheet
var channelBox = app.getElementById('channelBox');
var verticalBox = app.getElementById('verticalBox');
var langBox = app.getElementById('langBox');
var chan = e.parameter.channelBox;
var vert = e.parameter.verticalBox;
langBox.clear();
var langFilter = sheetList4.getRange('A1').setFormula('=iferror(filter(boolean!A:D,boolean!A:A="' + chan + '",boolean!B:B="' + vert + '",boolean!D:D=TRUE))');
//**this returns as chan = undefined**
var numItemList2 = sheetList4.getLastRow();//get the item array
var list1ItemArray2 = sheetList4.getRange(1,3,numItemList2).getValues();//Add the items in ListBox
for(var i=0; i<list1ItemArray2.length; i++){langBox.addItem(list1ItemArray2[i][0]);}
if (chan == '--')
{langBox.clear();
var langArray = ['Select language','Dutch','French','German','Italian','Portuguese', 'Russian','Spanish'];
for (var i=0;i<langArray.length;++i) {langBox.addItem(langArray[i]);};}
return app
};
If anyone can use their magic eyes to give me any advice, it would be greatly appreciated!
The problem is with your callbackElement.
What I suggest is that you put both these list boxes into a single panel and add this panel as the callback element to both the handlers. Something like
function doGet(){
/* Your other code here */
var panel = app.createVerticalPanel().setId('panel');
var channelBox = app.createListBox().setId('channelBox').setName('channelBox').setWidth(152).setHeight(22);
var chanHandler = app.createServerChangeHandler('showChannelinfo');
chanHandler.addCallbackElement(panel); /* Add panel as the callback element */
channelBox.addChangeHandler(chanHandler);
var chaArray = ['Select channel','--','Email','Phone','Chat']; for (var i=0;i<chaArray.length;++i) {channelBox.addItem(chaArray[i]);};
var verticalBox = app.createListBox().setId('verticalBox').setName('verticalBox').setWidth(152).setHeight(22);
var vertHandler = app.createServerChangeHandler('showVerticalinfo');
vertHandler.addCallbackElement(panel); /* Add panel as the callback element */
verticalBox.addChangeHandler(vertHandler);
panel.add(channelBox);
panel.add(verticalBox);
/* Your other UI Code here */
}

Resources