I am developing a chrome extension and I need to implement a feature that allows to launch an .exe on button click AND pass user entered parameters to that .exe.
Feature details: User selects file and clicks on a button -> button triggers prompt menu, getting input from user -> if user provides input extension launches .exe file, passing user entered input to .exe's stdio stream -> .exe reads stdin and saves user entered input to txt file -> (optional) .exe sends success message to extension.
The problem is it does not work as expected and i have no idea what did i do wrong :(
Here is what i did and my results:
Button onClick logic:
content.js
button.addEventListener('click', function() {
allSelected = document.querySelectorAll('[aria-selected="true"]')
console.log(allSelected.length)
let allFIles = []
allSelected.forEach(element => {
// Get the parent element
const parentElement = element.parentNode;
// Check if the parent element has the data-id attribute
if (parentElement && parentElement.hasAttribute('data-id')) {
// Retrieve the value of the data-id attribute
const dataIdValue = parentElement.getAttribute('data-id');
console.log('Data ID value:', dataIdValue);
allFIles.push(dataIdValue)
} else {
console.log('Parent element does not have a data-id attribute');
}
});
const userInput = window.prompt(`You selected ${allFIles.length} files. Please, provide info on these files below.`, '');
// Check if the user entered something
if (userInput !== null) {
// User clicked OK and entered some text
console.log('User input:', userInput);
allInfo = {"fileIds": allFIles, "newValues": userInput}
chrome.runtime.sendMessage({ action: 'connectNative', data: allInfo});
} else {
// User clicked Cancel or closed the prompt dialog
console.log('User canceled input.');
}
});
In my background.js file I tried using both chrome.runtime.connectNative+port.postMessage or chrome.runtime.sendNativeMessage. Trough a lot of trial and error I came up with this block of code that seems to work (to some extent):
background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log('Message received:', request);
if (request.action === 'connectNative') {
console.log("try");
let port = chrome.runtime.connectNative('com.google_drive.edit_description');
port.postMessage({filesToEdit: request["data"]})
port.onMessage.addListener(function(message) {
console.log("Message from native messaging host:", message);
});
}
});
I created com.my_extension.new_feature folder in NativeMessagingHost and provided full path to native-apps/editInfo.json
editInfo.json
{
"name": "com.my_extension.new_feature",
"description": "My Application",
"path": "new_feature.exe",
"type": "stdio",
"allowed_origins": ["chrome-extension://<passed my_extension's id>/"]
}
I added nativeMessaging permissions to manifest.json. So far so good. Then i created my .exe file from Python script (this is my primary programming language)
new_feature.py
import sys
import json
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%d-%b-%y %H:%M:%S',
filename='new_feature.log',
filemode='a')
def main():
logging.info("Started")
input_data = sys.stdin.read()
logging.info("Read stdin")
logging.info(f"Input data: {input_data}")
with open('output.txt', 'w') as f:
f.write(input_data)
logging.info("Wrote to file")
send_message({"message": "Success"})
if __name__ == "__main__":
try:
main()
except Exception as e:
logging.error(e)
It looks like everything should be fine now, but this is what happens:
On button click my new_feature.exe is triggered. It writes to .log file 15-Feb-24 18:51:34 - root - INFO - Started and then it does nothing! I tried setting setTimeout before port.postMessage in background.js (just in case there are some time-related problems)- nothing changed, same results. But while experimenting I noticed that when I reload extension from in chrome://extensions/ it seems to "release" this part of code input_data = sys.stdin.read(), and successfuly runs the rest of the code, writing to .log
15-Feb-24 18:58:10 - root - INFO - Read stdin
15-Feb-24 18:58:10 - root - INFO - Input data: 6 {"filesToEdit":{"fileIds":[<fileIds>],"newValues":"<newValues>"}}
15-Feb-24 18:58:10 - root - INFO - Wrote to file
I don`t understand why this is happening and this does not solve my problem - I am not going to reload extension every time I want to use this feature. I read a lot of different answers here as well as everywhere else, including asking chatGPT. Nothing seemed to work.
Then I accidentally didn't comment the rest of the code when trying to send message from .exe to extension (why not...) AND IT WORKED. I have no idea why, here is the full code that reads stdin and writes to .log file with no problems:
new_feature.py
import sys
import json
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%d-%b-%y %H:%M:%S',
filename='edit_gd_description.log',
filemode='a')
def send_message(message):
sys.stdout.write(json.dumps(message))
sys.stdout.flush()
def main():
logging.info("Started")
send_message({"message": "Waiting for input"})
input_data = sys.stdin.read()
logging.info("Read stdin")
logging.info(f"Input data: {input_data}")
with open('output.txt', 'w') as f:
f.write(input_data)
logging.info("Wrote to file")
send_message({"message": "Success"})
if __name__ == "__main__":
try:
main()
except Exception as e:
logging.error(e)
This logs Unchecked runtime.lastError: Error when communicating with the native messaging host. to background.js console, but the .exe works well, getting right input from stdin that extesion sends, and writing this input to .log and .txt.
I would like to know why is that happening, what did I do wrong? How can i make .exe send messages to extension?
Thank you for any help or ideas!
Here's your working code based on https://github.com/guest271314/NativeMessagingHosts/blob/main/nm_python.py. Native Messaging host uses a loop to keep the host active and not exit.
Nowhere in your code do you parse the message length, which happens in the below code in
getMessageandencodeMessage.