Terminate soffice.bin & soffice.exe when loadComponentFromURL is taking time or hung

#include<Windows.h>
#include <stdio.h>

#include <sal/main.h>
#include <cppuhelper/bootstrap.hxx>
#include <com/sun/star/bridge/XUnoUrlResolver.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/XComponentLoader.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <osl/file.hxx>
#include <com/sun/star/util/XCloseable.hpp>
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/view/XPrintable.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/awt/Size.hpp>

#include<string>
#include<iostream>
#include <chrono>

using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::frame;
using namespace com::sun::star::beans;
using namespace com::sun::star::view;
using namespace com::sun::star::style;
using namespace com::sun::star::container;
using namespace com::sun::star::awt;

using ::rtl::OUString;
using ::rtl::OUStringToOString;
using namespace com::sun::star::util;

OUString getPdfExportFilterName(Reference<XComponent>& xComponent)
{
   Reference<XServiceInfo> xServiceInfo(xComponent, UNO_QUERY);
   if (!xServiceInfo.is())
   {
      std::cerr << "Failed to get XServiceInfo." << std::endl;
      return "";
   }

   if (xServiceInfo->supportsService("com.sun.star.text.TextDocument"))
      return OUString::createFromAscii("writer_pdf_Export");
   else if (xServiceInfo->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
      return OUString::createFromAscii("calc_pdf_Export");
   else if (xServiceInfo->supportsService("com.sun.star.presentation.PresentationDocument"))
      return OUString::createFromAscii("impress_pdf_Export");
   else if (xServiceInfo->supportsService("com.sun.star.drawing.DrawingDocument"))
      return OUString::createFromAscii("draw_pdf_Export");
   else if (xServiceInfo->supportsService("com.sun.star.formula.FormulaProperties"))
      return OUString::createFromAscii("math_pdf_Export");
   else
      throw std::runtime_error("Unsupported document type");
}
bool setPageLayout(Reference<XComponent> xComponent, Size pageSize, bool isLandscape)
{
   if (!xComponent.is()) return false;

   // Try Writer-style modification
   try {
      Reference<XStyleFamiliesSupplier> xSupplier(xComponent, UNO_QUERY);
      if (xSupplier.is()) {
         Reference<XNameAccess> xFamilies = xSupplier->getStyleFamilies();
         if (xFamilies->hasByName("PageStyles")) {
            Any aPageStyles = xFamilies->getByName("PageStyles");
            Reference<XNameAccess> xPageStyles;
            aPageStyles >>= xPageStyles;

            if (xPageStyles->hasByName("Standard")) {
               Any aStyle = xPageStyles->getByName("Standard");
               Reference<XPropertySet> xStyleProps;
               aStyle >>= xStyleProps;

               xStyleProps->setPropertyValue("IsLandscape", Any(isLandscape));
               xStyleProps->setPropertyValue("Size", Any(pageSize));
               return true;
            }
            else {
               std::cerr << "\"Standard\" page style not found." << std::endl;
            }
         }
      }
   }
   catch (...) {
      std::cerr << "Writer-style layout adjustment failed. Trying XPrintable..." << std::endl;
   }

   // Try XPrintable (Calc, generic fallback)
   try {
      Reference<XPrintable> xPrintable(xComponent, UNO_QUERY);
      if (xPrintable.is()) {
         PropertyValue printProps[2];

         printProps[0].Name = OUString::createFromAscii("IsLandscape");
         printProps[0].Value <<= isLandscape;

         printProps[1].Name = OUString::createFromAscii("PaperSize");
         printProps[1].Value <<= pageSize;

         Sequence<PropertyValue> propsSeq(printProps, 2);
         xPrintable->setPrinter(propsSeq);
         return true;
      }
   }
   catch (...)
   {
      std::cerr << "XPrintable layout adjustment failed." << std::endl;
   }

   std::cerr << "Document type not supported or layout change failed." << std::endl;
   return false;
}
SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
{

   if (argc < 3) {
      fprintf(stderr, "Usage:  < input_file_path > <output_file_path>\n");
      return 1;
   }
   auto doc_convert_start = std::chrono::high_resolution_clock::now();

   //Convert system paths (argv[1] and argv[2]) to OUString
   OUString sInputPath = OUString::createFromAscii(argv[1]);
   OUString sOutputPath = OUString::createFromAscii(argv[2]);

   // Convert to file:// URL format recognized by LibreOffice
   OUString sInputFileURL, sOutputFileURL;
   if (osl::FileBase::getFileURLFromSystemPath(sInputPath, sInputFileURL) != osl::FileBase::E_None ||
      osl::FileBase::getFileURLFromSystemPath(sOutputPath, sOutputFileURL) != osl::FileBase::E_None) {
      std::cerr << "Error: Could not convert file paths to file URLs.\n";
      return 1;
   }

   try
   {

      //std::string uid = std::to_string(GetCurrentProcessId());
      //std::string profile = "file:///C:/Temp/lo-profile-" + uid;
      //std::string envVar = "UserInstallation=" + profile;
      //_putenv(envVar.c_str());
      
      std::cout << "Bootstrap started" << std::endl;
      auto startTime = std::chrono::high_resolution_clock::now();
      // get the remote office component context
      Reference< XComponentContext > xContext(::cppu::bootstrap());
      if (!xContext.is())
      {
         fprintf(stderr, "no component context!\n");
         return 1;
      }
      std::cout << "Bootstrap Completed" << std::endl;
      auto endTime = std::chrono::high_resolution_clock::now();
      auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
      std::cout << "Time taken for Bootstraping: " << duration << " milliseconds" << std::endl;

      // get the remote office service manager
      Reference< XMultiComponentFactory > xServiceManager(
         xContext->getServiceManager());
      if (!xServiceManager.is())
      {
         fprintf(stderr, "no service manager!\n");
         return 1;
      }

      // get an instance of the remote office desktop UNO service
      // and query the XComponentLoader interface
      Reference < XDesktop2 > xComponentLoader = Desktop::create(xContext);


      Sequence<PropertyValue> loadProperties(2);

      // Property 1: Open in hidden mode
      loadProperties[0].Name = OUString::createFromAscii("Hidden");
      loadProperties[0].Value <<= true;

      // Property 2: Open in read-only mode
      loadProperties[1].Name = OUString::createFromAscii("ReadOnly");
      loadProperties[1].Value <<= true;

      std::cout << "Document Load Started" << std::endl;
      // open a spreadsheet document
      Reference< XComponent > xComponent(xComponentLoader->loadComponentFromURL(
         sInputFileURL,
         OUString("_blank"), 0,
         loadProperties));
      if (!xComponent.is())
      {
         fprintf(stderr, "opening  document failed!\n");
         return 1;
      }
      std::cout << "Document Loaded successfully" << std::endl;

      OUString filterName = getPdfExportFilterName(xComponent);
      std::wcout << L"filterName " << filterName.getStr() << std::endl;;

      Size tabloidSize;
      tabloidSize.Width = 43180;  // 17 in
      tabloidSize.Height = 27940; // 11 in
      setPageLayout(xComponent, tabloidSize, true);


      // Export as PDF
      Sequence<PropertyValue> storeProps(1);
      storeProps[0].Name = OUString::createFromAscii("FilterName");
      storeProps[0].Value <<= filterName; //OUString::createFromAscii("writer_pdf_Export");

      std::cout << "Document storing to pdf Started" << std::endl;
      Reference<XStorable> xStorable(xComponent, UNO_QUERY);
      xStorable->storeToURL(sOutputFileURL, storeProps);
      std::cout << "Document storing to pdf completed successfully" << std::endl;

      Reference<XCloseable> xCloseable(xComponent, UNO_QUERY);
      if (xCloseable.is()) xCloseable->close(true);

      if(xComponentLoader.is())
         xComponentLoader->terminate();

      // Clear references
      xCloseable.clear();
      xStorable.clear();
      xComponent.clear();
      xComponentLoader.clear();
      xServiceManager.clear();
      xContext.clear();

      auto doc_convert_end = std::chrono::high_resolution_clock::now();

      auto doc_convert_duration = std::chrono::duration_cast<std::chrono::seconds>(doc_convert_end - doc_convert_start);
      int doc_convert_total_seconds = doc_convert_duration.count();
      int doc_convert_minutes = doc_convert_total_seconds / 60;
      int doc_convert_seconds = doc_convert_total_seconds % 60;

      std::cout << "doc_convert_duration: " << doc_convert_minutes << " minute(s) and "
         << doc_convert_seconds << " second(s)" << std::endl;

      return 0;
   }
   catch (::cppu::BootstrapException& e)
   {
      fprintf(stderr, "caught BootstrapException: %s\n",
         OUStringToOString(e.getMessage(), RTL_TEXTENCODING_ASCII_US).getStr());
      return 1;
   }
   catch (Exception& e)
   {
      fprintf(stderr, "caught UNO exception: %s\n",
         OUStringToOString(e.Message, RTL_TEXTENCODING_ASCII_US).getStr());
      return 1;
   }

  
}

I would like to **terminate the soffice.bin and soffice.exe** if **loadComponentFromURL** or **storeToURL** are taking more time than expected. I just moved all the code to a different thread and waiting on thread for a defined timeout then trying to call the  
 if(xComponentLoader.is())
     xComponentLoader->terminate(); but this is not able to terminate the soffice.bin or soffice.exe

Is there a way to do this ? Please let me know.

@mikekaganski Any chance you know something about it?

Use system means to kill processes.

@mikekaganski I am running multiple instances of the above program at the same time, so it triggers multiple soffice’s and working fine without any issue. I don’t know which soffice to kill in this case as I don’t know the process id.

You are welcome to file an enhancement request to provide means to get the process id from the component context.

This may be a workaround:
https://stackoverflow.com/questions/62365978/get-a-list-of-child-processes-from-parent-process-in-c-and-c-cross-platform

Created a Enhancement request 167269 – Return the process ID of soffice from the component context.