Search XLSX File

Search XLSX File

DGCSCAD
Collaborator Collaborator
795 Views
16 Replies
Message 1 of 17

Search XLSX File

DGCSCAD
Collaborator
Collaborator

My apologies if this has been answered somewhere, but I can't seem to find much on this topic. I'm looking to:

 

  1. Open an Excel.xlsx to the first sheet.
  2. Search for a specific text string located anywhere in the first column.
  3. Return the Row #.

I have GetExcel.lsp but there's nothing about searching the file for a string in that code. If there is, I'm not seeing it.

 

Also, how have others handled AutoCAD locking up if the Excel file is open when trying to access it from within a LISP function?

AutoCad 2018 (full)
Win 11 Pro
Accepted solutions (1)
796 Views
16 Replies
Replies (16)
Message 2 of 17

DGCSCAD
Collaborator
Collaborator

I did find this:

https://www.cadtutor.net/forum/topic/68859-lisp-to-open-an-excel-file-and-search-content/page/3/

 

...which is a good place to start.

AutoCad 2018 (full)
Win 11 Pro
Message 3 of 17

5w1tch2
Explorer
Explorer

Would this be close to what you had in mind? Took inspiration from paullimapa, (here).

 

(defun c:FINDEXCELROW (/ *error* close-xlsx excelApp workbook workbooks sheets sheet cell
                        file target row found value)

  (vl-load-com)

  ;; ERROR HANDLE
  (defun *error* (msg)
    (princ "\n[FINDEXCELROW] Program Error: ")
    (princ msg)
    (close-xlsx)
    (princ)
  )

  ;; CLEAN UP
  (defun close-xlsx ()
    (foreach obj (list workbook workbooks excelApp)
      (if (and obj (not (vl-catch-all-error-p obj)))
        (vl-catch-all-apply 'vlax-release-object (list obj))
      )
    )
    ;; QUIT EXCEL IF RUNNING
    (if (and excelApp (not (vl-catch-all-error-p excelApp)))
      (vl-catch-all-apply 'vlax-invoke-method (list excelApp 'Quit))
    )
  )

  ;; PROMPT
  (setq target (getstring T "\nType the text to find in Column A: "))
  (if (or (null target) (= target ""))
    (progn (princ "\nNo search text entered. Cancelled.") (exit))
  )
  (setq file (getfiled "Select Excel File to Search" "" "xlsx" 0))
  (if (not (and file (findfile file)))
    (progn (princ "\nNo valid file selected. Cancelled.") (exit))
  )

  (princ (strcat "\n[FINDEXCELROW] Opening Excel file: " file))

  ;; EA
  (setq excelApp (vlax-get-or-create-object "Excel.Application"))
  (setq workbooks (vlax-get excelApp 'Workbooks))
  (setq workbook (vl-catch-all-apply 'vlax-invoke-method (list workbooks 'Open file)))
  (if (vl-catch-all-error-p workbook)
    (progn
      (princ "\nCan't access file. Is it open in Excel, or locked?")
      (close-xlsx)
      (exit)
    )
  )

  (setq sheets (vlax-get workbook 'Sheets))
  (setq sheet (vlax-get-property sheets 'Item 1)) ; Always checks the first sheet

  ;; SEARCH ROW
  (setq row 1 found nil)
  (while (not found)
    (setq cell (vlax-get-property (vlax-get-property sheet 'Cells) 'Item row 1))
    (setq value (vl-catch-all-apply 'vlax-get-property (list (vlax-variant-value cell) 'Value)))
    (if (vl-catch-all-error-p value)
      (setq value nil)
      (setq value (vlax-variant-value value))
    )
    (cond
      ((null value)
        (setq found "Not found") ; End of data, bail out
      )
      ((= (strcase (vl-princ-to-string value)) (strcase target))
        (setq found row) ; FOUND!
      )
      (T
        (setq row (1+ row)) ; Try next row
      )
    )
  )
  (close-xlsx)
  (if (numberp found)
    (princ (strcat "\n[FINDEXCELROW] Success! '" target "' found at row " (itoa found) "."))
    (princ (strcat "\n[FINDEXCELROW] '" target "' was NOT found in Column A."))
  )

  (princ)
)

 

0 Likes
Message 4 of 17

DGCSCAD
Collaborator
Collaborator

Thanks for that. I really appreciate the help, but it's not finding my test string and the clean up portion of it isn't working correctly. It's leaving a lock file that I can't delete and remains until I reboot.

 

I'm getting close with this:

; RLX - 10-10-2019 - last update 25-oct-2019
(defun c:Sam ( / *AcApp* *ActDoc* OldErr cell-values *excel-app* *excel-workbooks* *excel-openworkbook*
                 *excel-activesheet* *Excel-MaxRow* *Excel-MaxCol*)(_Init)(_Start)(_Exit)(princ))
(defun _Init ()(setq *error* _Err *AcApp* (vlax-get-acad-object) *ActDoc* (vla-get-activedocument *AcApp*)))
(defun _Start ( / search-text inp result end-result rtn my-choise)
  (if (and (setq search-text (getstring "\nEnter number to search : "))(not (void search-text)))
    (setq search-text (strcase search-text))(setq search-text nil))
  (if search-text
    (progn
      (princ "\n(1) - search 1-Zone, (2) - search 2-Zone, (3) - both <3> : ")
      (setq inp (vl-catch-all-apply 'grread (list nil 8 0)))
      (textscr)
      (cond
        ((vl-catch-all-error-p inp)(princ "\nSelection cancelled\n")(setq result nil))
        ((equal inp '(2 49))(princ " 1 \n")(setq result (Read_1_Zone)))
        ((equal inp '(2 50))(princ " 2 \n")(setq result (Read_2_Zone)))
        ((or (equal inp '(2 51)) (equal inp '(2 13)) (equal inp '(2 32)) (equal (car inp) 25))
         (princ " both \n")(setq result (append (Read_1_Zone) (Read_2_Zone))))
        (t (princ "\nSearch aborted\n")(setq result nil))
      )
      (setq end-result (find_search_text search-text result))
      (cond
        ((void end-result)(alert "\nSearch text not found\n"))
        ((> (length end-result) 1) (princ "\nMultiple results found\n")
         (if (and (setq rtn (cfl end-result)) (vl-file-directory-p rtn))
           (progn (princ (strcat "\nOpening folder : " rtn))(startapp "explorer" rtn))
           (princ (strcat "\nFolder not found : " (vl-princ-to-string rtn) "\n"))))
        ((not (vl-file-directory-p (car end-result)))
         (princ (strcat "\nUnable to find folder : " (car end-result))))
        ((vl-file-directory-p (car end-result))
         (princ (strcat "\nOpening folder : " (car end-result)))(startapp "explorer" (car end-result)))
        (t (princ "\nHyperlink selection cancelled or unable to find folder\n"))
      )
    )
    (princ "\nSearch aborted\n")
  )
  (princ)
)

(defun Read_1_Zone ( / f r)
  (if (setq f (findfile "G:\\JEI CAD Support\\AutoCad Support\\Development\\DataLink\\Excel_Test_001.xlsx"))
    (setq r (Read_ExcelFile f "D" '("F" "H" "J" "L" "N")))(progn (princ "\n1- ZONE not found")(setq r nil))) r)



(defun Read_2_Zone ( / f r)
  (if (setq f (findfile "G:\\JEI CAD Support\\AutoCad Support\\Development\\DataLink\\Excel_Test_001.xlsx"))
    (setq r (Read_ExcelFile f "B" '("C" "E" "G" "I" "K")))(progn (princ "\n2- ZONE not found")(setq r nil))) r)



(defun find_search_text (s l / r) (foreach x l (if (vl-string-search s (car x)) (setq r (cons (cadr x) r)))) r)

; $fn = xlsl filename, $hlc = hyperlink column, %col-lst = list of column names
; in this case I use the first and last column letter from %col-lst i.e. '("F" "H" "J" "L" "N")
(defun Read_ExcelFile ($fn $hlc %col-lst / *Excel-MaxRow* *Excel-MaxCol* *range* i cell-values hyperlink result-list)
  (if (and $fn (setq $fn (findfile $fn))) (_OpenWorkbook $fn))
  (if (and *excel-app* *excel-openworkbook* *excel-activesheet*)
    (progn
      (prompt (strcat "\nReading from : " $fn "\n"))
      (setq *range* (vlax-get-property  *excel-activesheet* 'UsedRange))
      (prompt (strcat "\nNumber of rows in sheet : " (vl-princ-to-string (setq *Excel-MaxRow* (vlax-get-property
        (vlax-get-property *range* "Rows") "Count"))) " , Number of columns in sheet : " (vl-princ-to-string
          (setq *Excel-MaxCol* (vlax-get-property (vlax-get-property *range* "Columns") "Count"))) "\nScanning...\n"))
    )
  )
  ; get all the cell values
  ; if first row are labels for example , first data-row (i) = 2 , else i = 1
  (setq i 1)
  (if *Excel-MaxRow*
    ;(1- *Excel-MaxRow*) if data starts on 2nd row
    (repeat *Excel-MaxRow*
      (and
        ; check if hyperlink is found, if not, no need to look any further
        ;(setq hyperlink (Excel_GetCellHyperlinks (strcat $hlc (itoa i) ":" $hlc (itoa i))));;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        ; read entire range from Label-Column (car Label-Column+row) : (last Label-Column+row)
        ; other possibility would be read each column and convert with strcat to one string
        (vl-consp (setq cell-values (Excel_GetCellValue
           (strcat (car %col-lst) (itoa i) ":" (last %col-lst) (itoa i)))))
        (setq result-list (cons (cons (apply 'strcat (mapcar 'strcase cell-values)) hyperlink) result-list))
	(princ cell-values)
      )
      (setq i (1+ i))
    )
  )
  result-list
)

(defun _OpenWorkbook ( fn / )
  (cond
    ((or (void fn) (not (eq (type fn) 'STR)))
     (alert (strcat "Invalid filename for workbook" (vl-princ-to-string fn))))
    ((not (findfile fn))(alert (strcat "Unable to locate excel workbook :\n" fn)))
    ((vl-catch-all-error-p (setq *excel-app* (vl-catch-all-apply 'vlax-get-or-create-object '("excel.application"))))
     (alert (strcat "\nError intializing Excel application :\n" (vl-catch-all-error-message *excel-app*))))
    ((vl-catch-all-error-p (setq *excel-workbooks* (vl-catch-all-apply 'vlax-get-property (list *excel-app* 'workbooks))))
     (alert (strcat "Error intializing Excel workbooks :\n" (vl-catch-all-error-message *excel-workbooks*))))
    ((vl-catch-all-error-p (setq *excel-openworkbook* (vl-catch-all-apply 'vlax-invoke-method
       (list *excel-workbooks* "open" fn 1 1 1 (vlax-make-variant "rlx")))))
     (alert (strcat "Error opening Excel workbook collection :\n" (vl-catch-all-error-message *excel-openworkbook*))))                         
    ((vl-catch-all-error-p (setq *excel-activesheet* (vl-catch-all-apply 'vlax-get-property (list *excel-app* 'activesheet))))
     (alert (strcat "Error opening Excel active workbook :\n" (vl-catch-all-error-message *excel-activesheet*))))
    ((vl-catch-all-error-p (setq err (vl-catch-all-apply 'vlax-invoke-method (list *excel-activesheet* 'activate))))
     (alert (strcat "Unable to activate active sheet :\n" (vl-catch-all-error-message err))))
    (t (vla-put-visible *excel-app* :vlax-false)(princ "\nExcel opened\n"))
  )
)

(defun _CloseExcel ()
  (if *excel-app* (progn (vl-catch-all-apply 'vla-quit (list *excel-app*))(_ReleaseExcel)(princ "\nExcel closed\n"))))

(defun _ReleaseExcel ()
  (mapcar '(lambda(x) (if x (vl-catch-all-apply 'vlax-release-object (list x)))(setq x nil))
           (list *excel-activesheet* *excel-openworkbook* *excel-workbooks* *excel-app*)) (gc))

; assumes excel is up & running
(defun Excel_GetCellValue ( %range / ExcelRange range ExcelVariant ExcelValue etype)
  (if *excel-app*
    (progn
      ; change single cell range to multiculti cell i.e. "A1" -> "A1:A1"
      (if (not (wcmatch %range "*:*"))(setq range (strcat %range ":" %range))(setq range %range))
      (setq ExcelRange (vlax-get-property *excel-app* "Range" range))
      (setq ExcelVariant (vl-catch-all-apply (function (lambda ()(vlax-get-property ExcelRange 'Value)))))
      (setq ExcelValue (vl-catch-all-apply (function (lambda ()(vlax-variant-value ExcelVariant)))))
      (if ExcelValue
 (setq etype (type ExcelValue)
       ExcelValue
               (cond ((= etype 'INT) (itoa ExcelValue))
                     ((= etype 'REAL) (rtos ExcelValue 2 2))
                     ((= etype 'STR) (vl-string-trim " " ExcelValue))
                     ((= etype 'safearray) (vl-remove "nil" (mapcar 'vl-princ-to-string (mapcar
                      '(lambda (s) (vlax-variant-value s))(car (vlax-safearray->list excelvalue))))))
                     ((/= etype 'STR) "")))
        (setq ExcelValue "")
      )
    )
  )
  ; change all nil's to "Empty"
  (if (listp ExcelValue) (setq ExcelValue (subst "Empty" nil ExcelValue)))
  ExcelValue
)

(defun Excel_GetCellHyperlinks ( %range / ExcelRange range ExcelHyperlinks hyperlist)
  (if *excel-app*
    (progn
      ; change single cell range to multiculti cell i.e. "A1" -> "A1:A1"
      (if (not (wcmatch %range "*:*"))(setq range (strcat %range ":" %range))(setq range %range))
      (setq ExcelRange (vlax-get-property *excel-app* "Range" range))
      (setq ExcelHyperlinks (vl-catch-all-apply (function (lambda ()(vlax-get-property ExcelRange 'hyperlinks)))))
      (vlax-for url ExcelHyperlinks
        (if (not (vl-catch-all-error-p (setq ExcelAddress
           (vl-catch-all-apply (function (lambda () (vlax-get-property url 'Address)))))))
          (setq hyperlist (cons ExcelAddress hyperlist))(setq hyperlist (cons "hyper-error" hyperlist))
        )
      )
    )
  )
  hyperlist
)

(defun cfl (l / f p d r)
  (and (setq p (open (setq f (vl-filename-mktemp ".dcl")) "w"))
       (princ "cfl:dialog{label=\"Choose\";:list_box{width=80;key=\"lb\";}ok_cancel;}" p)
       (not (setq p (close p)))(< 0 (setq d (load_dialog f)))(new_dialog "cfl" d)
       (progn
         (start_list "lb")(mapcar 'add_list l)(end_list)
          (action_tile "lb" "(setq r (nth (atoi $value) l))(done_dialog 1)")
           (action_tile "accept" "(setq r (get_tile \"lb\"))(done_dialog 1)")
            (action_tile "cancel" "(setq r nil)(done_dialog 0)")
             (start_dialog)(unload_dialog d)(vl-file-delete f)))
  (cond ((= r "") nil)(r r)(t nil))
)

(defun _Err ($s) (term_dialog) (princ $s) (_Exit) (setq *error* OldErr) (princ))
(defun _Exit ()  (_CloseExcel) (setq *error* OldErr) (princ))
(defun void (x) (or (null x) (and (eq 'STR (type x)) (eq "" (vl-string-trim " \t\r\n" x)))))

(princ (strcat "\n\nSearch excel file for string and open hyperlink"
               "\nWritten for SamBuddy by RLX 25-Oct-2019 - type sam or (c:sam) after loading"))
(vl-load-com)
(princ)

 

As it is, I'm having trouble deciphering the "Zones" and the search starts at column C, which I'm trying to sort out. This was created to search for hyperlinks, but the search functionality is there, just gotta steer it in my direction. Here's the portion I'm concentrating on:

 

  ; get all the cell values
  ; if first row are labels for example , first data-row (i) = 2 , else i = 1
  (setq i 1)
  (if *Excel-MaxRow*
    ;(1- *Excel-MaxRow*) if data starts on 2nd row
    (repeat *Excel-MaxRow*
      (and
        ; check if hyperlink is found, if not, no need to look any further
        ;(setq hyperlink (Excel_GetCellHyperlinks (strcat $hlc (itoa i) ":" $hlc (itoa i))));;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        ; read entire range from Label-Column (car Label-Column+row) : (last Label-Column+row)
        ; other possibility would be read each column and convert with strcat to one string
        (vl-consp (setq cell-values (Excel_GetCellValue
           (strcat (car %col-lst) (itoa i) ":" (last %col-lst) (itoa i)))))
        (setq result-list (cons (cons (apply 'strcat (mapcar 'strcase cell-values)) hyperlink) result-list))
	(princ cell-values)
      )
      (setq i (1+ i))
    )
  )

 

 

 

AutoCad 2018 (full)
Win 11 Pro
0 Likes
Message 5 of 17

5w1tch2
Explorer
Explorer

Interesting! Thanks for the info.. sounds like you're on the right path. Could you send over an example Excel file that shows your setup? Is the text string you're searching for always the same?

Just to make sure I’m on the same page.. You're trying to open an .xlsx file, look at the first sheet, search for a specific text string(always in column A?), and get the row number (or numbers??) where it appears.. Is that right?

0 Likes
Message 6 of 17

DGCSCAD
Collaborator
Collaborator

Is the text string you're searching for always the same?

I'm using "12345" for now, but the string will always be different.

 

Could you send over an example Excel file that shows your setup?

Attached.

 

Just to make sure I’m on the same page.. You're trying to open an .xlsx file, look at the first sheet, search for a specific text string(always in column A?)

Correct.

 

, and get the row number (or numbers??) where it appears.. Is that right?

Correct.

 

Also, if the string is NOT found, I need the row # of the first available blank cell in column A.

AutoCad 2018 (full)
Win 11 Pro
0 Likes
Message 7 of 17

5w1tch2
Explorer
Explorer
Accepted solution

Perfect, thanks. Try giving this a shot and see if it works.. It did for me on the test file.

(defun c:FINDEXCELROW (/ *excel-app* *excel-workbooks* *excel-openworkbook* *excel-activesheet*
                        file search-text search-val row foundRow blankRow cell-value empty-count)

  (vl-load-com)

  ;; HELPER : NORMALIZE STRINGS FOR COMPARE (trim, uppercase, drop trailing .0/zeros) ---
  (defun _normalize (s / out)
    (setq out (vl-princ-to-string (vl-string-trim " \t\r\n" s)))
    (setq out (strcase out))
    (if (wcmatch out "*.*")
      (progn
        (setq out (vl-string-right-trim "0" out))
        (setq out (vl-string-right-trim "." out))
      )
    )
    out
  )

  (setq search-text (getstring T "\nEnter text to find in Column A: "))
  (if (or (null search-text) (= search-text ""))
    (progn (princ "\nNo search text entered. Cancelled.") (exit)))

  (setq file (getfiled "Select Excel File to Search" "" "xlsx" 0))
  (if (not (and file (findfile file)))
    (progn (princ "\nNo valid file selected. Cancelled.") (exit)))

  (setq *excel-app* (vlax-get-or-create-object "Excel.Application"))
  (vla-put-visible *excel-app* :vlax-false)
  (setq *excel-workbooks* (vlax-get-property *excel-app* 'Workbooks))
  (setq *excel-openworkbook* (vlax-invoke-method *excel-workbooks* 'Open file))
  (setq *excel-activesheet* (vlax-get-property *excel-openworkbook* 'ActiveSheet))

  (setq row 2
        foundRow nil
        blankRow nil
        empty-count 0
        search-val (_normalize search-text))

  (while (and (< row 10000) (< empty-count 2) (not foundRow))
    (setq cell-value (Excel_GetCellValue (strcat "A" (itoa row))))
    (setq cell-value
      (cond
        ((null cell-value) "")
        ((listp cell-value) (car cell-value))
        (T cell-value)
      )
    )
    (setq cell-value (_normalize cell-value))
    (cond
      ((= cell-value "") ; blank cell
        (if (not blankRow) (setq blankRow row))
        (setq empty-count (1+ empty-count))
      )
      ((= cell-value search-val)
        (setq foundRow row))
      (T
        (setq empty-count 0)) ; not blank, reset counter
    )
    (setq row (1+ row))
  )

  ;; CLEAN UP, NO LOCK (FINGERS CROSSED)
  (vl-catch-all-apply 'vlax-release-object (list *excel-activesheet*))
  (vl-catch-all-apply 'vlax-release-object (list *excel-openworkbook*))
  (vl-catch-all-apply 'vlax-release-object (list *excel-workbooks*))
  (if *excel-app* (progn (vlax-invoke-method *excel-app* 'Quit) (vlax-release-object *excel-app*)))
  (gc)

  (cond
    (foundRow (princ (strcat "\n[FINDEXCELROW] '" search-text "' found at row " (itoa foundRow) ".\n")))
    (blankRow (princ (strcat "\n[FINDEXCELROW] '" search-text "' not found. First blank cell at row " (itoa blankRow) ".\n")))
    (T (princ "\n[FINDEXCELROW] No match or blank cell found.\n"))
  )
  (princ)
)

(defun Excel_GetCellValue ( %range / ExcelRange range ExcelVariant ExcelValue etype)
  (if *excel-app*
    (progn
      (if (not (wcmatch %range "*:*"))
        (setq range (strcat %range ":" %range))
        (setq range %range)
      )
      (setq ExcelRange   (vlax-get-property *excel-app* "Range" range))
      (setq ExcelVariant (vl-catch-all-apply (function (lambda () (vlax-get-property ExcelRange 'Value)))))
      (setq ExcelValue   (vl-catch-all-apply (function (lambda () (vlax-variant-value ExcelVariant)))))
      (if ExcelValue
        (progn
          (setq etype (type ExcelValue))
          (setq ExcelValue
            (cond
              ((= etype 'INT)  (itoa ExcelValue))
              ;; --- CHANGED: if REAL is whole, return as integer string; else keep decimals ---
              ((= etype 'REAL)
               (if (= ExcelValue (fix ExcelValue))
                 (itoa (fix ExcelValue))         ; e.g. 12345
                 (rtos ExcelValue 2 8)           ; keep precision for non-integers
               )
              )
              ((= etype 'STR)  (vl-string-trim " \t\r\n" ExcelValue))
              ((= etype 'safearray)
               (vl-remove "nil"
                 (mapcar 'vl-princ-to-string
                   (mapcar '(lambda (s) (vlax-variant-value s))
                     (car (vlax-safearray->list ExcelValue))
                   )
                 )
               )
              )
              ((/= etype 'STR) "")
            )
          )
        )
        (setq ExcelValue "")
      )
    )
  )
  (if (listp ExcelValue) (setq ExcelValue (subst "Empty" nil ExcelValue)))
  ExcelValue
)
0 Likes
Message 8 of 17

DGCSCAD
Collaborator
Collaborator

Yes, that worked:

 

Command: FINDEXCELROW

Enter text to find in Column A: 12345

[FINDEXCELROW] '12345' found at row 16.


Command:
FINDEXCELROW

Enter text to find in Column A:

[FINDEXCELROW] ' ' not found. First blank cell at row 17.

Command:

 

Thank you!

AutoCad 2018 (full)
Win 11 Pro
Message 9 of 17

5w1tch2
Explorer
Explorer

Happy to help

0 Likes
Message 10 of 17

DGCSCAD
Collaborator
Collaborator

For this follow-up question in my OP:

 

Also, how have others handled AutoCAD locking up if the Excel file is open when trying to access it from within a LISP function?

 

I'm thinking just search for the xlsx file using: (findfile (strcat pathtoexcelfile "~$" nameofexcelfile))

 

...unless someone has a better solution.

AutoCad 2018 (full)
Win 11 Pro
0 Likes
Message 11 of 17

5w1tch2
Explorer
Explorer

I haven't run into that issue. Maybe a read only function will change it?.. That way it wont grab a write lock. Long shot

(setq *excel-openworkbook*
  (vlax-invoke-method
    *excel-workbooks* 'Open
    file        ;; Filename
    0           ;; UpdateLinks := 0 (don't update external links)
    :vlax-true  ;; ReadOnly    := True
  )
)

 

0 Likes
Message 12 of 17

Sea-Haven
Mentor
Mentor

Just a comment,  if you use the property Usedrange you can get the maximum row and column. So will never get to 10000 etc.

(while (and (< row 10000) (< empty-count 2) (not foundRow))
; one line out of a defun to get row & column as numbers.
(setq myrange (vlax-get-property (vlax-get-property (vlax-get-property  myxl "ActiveSheet") 'UsedRange) 'address))

 I started with Getexcel but needed more so have this, it has a lot more functions than Getxecl, added as requests from here or other forums. Unfortunately a Wiz at Excel "Fixo" is no longer with us some of his code is in this code. Note it is defuns not "use this". There is a couple of other people around who have some great functions also. I need to tidy it up.

0 Likes
Message 13 of 17

DGCSCAD
Collaborator
Collaborator

@5w1tch2 wrote:

I haven't run into that issue. Maybe a read only function will change it?.. That way it wont grab a write lock. Long shot

(setq *excel-openworkbook*
  (vlax-invoke-method
    *excel-workbooks* 'Open
    file        ;; Filename
    0           ;; UpdateLinks := 0 (don't update external links)
    :vlax-true  ;; ReadOnly    := True
  )
)

 


There are multiple people editing the data in multiple files (all for different customers) constantly, so I'd need to check. Having users AutoCAD lock up is absolutely out of the question and has happened 100% of the time when I try to write-access an Excel file via LISP while it's open.

 

I replaced this with that :

(setq *excel-openworkbook* (vlax-invoke-method *excel-workbooks* 'Open file))

 

...and it reads and closes the already open file without issue. What I'm needing is just a check to see if someone is in the file. I'll be writing data to these and since we're not using any sort of Share Point, there can only be one session open for editing.

AutoCad 2018 (full)
Win 11 Pro
0 Likes
Message 14 of 17

DGCSCAD
Collaborator
Collaborator

@Sea-Haven wrote:

Just a comment,  if you use the property Usedrange you can get the maximum row and column. So will never get to 10000 etc.

(while (and (< row 10000) (< empty-count 2) (not foundRow))
; one line out of a defun to get row & column as numbers.
(setq myrange (vlax-get-property (vlax-get-property (vlax-get-property  myxl "ActiveSheet") 'UsedRange) 'address))

 I started with Getexcel but needed more so have this, it has a lot more functions than Getxecl, added as requests from here or other forums. Unfortunately a Wiz at Excel "Fixo" is no longer with us some of his code is in this code. Note it is defuns not "use this". There is a couple of other people around who have some great functions also. I need to tidy it up.


Thanks for that Alan. I did change that value to a higher number since it is possible to exceed it. I'll tinker around with those. Thank you.

 

I remember Fixo. I believe the phrase "Fixo'ed" was coined in his honor over where the crocodiles gather.

AutoCad 2018 (full)
Win 11 Pro
0 Likes
Message 15 of 17

5w1tch2
Explorer
Explorer

"What I'm needing is just a check to see if someone is in the file."

This should work as the (check if in edit) function. I tested it and it ignores if the file is open read only on another system. Interested to see if this works for your needs..

 

;; BUILD THE EXPECTED LOCKFILE PATH FOR AN .xlsx
(defun EXCEL-LockFilePath (xlsx)
  (strcat (vl-filename-directory xlsx) "\\~$"
          (vl-filename-base xlsx) (vl-filename-extension xlsx)))

;; RETURN T IF / SOMEONE IS EDITING THE FILE (lock file exists), NO? (nil)
(defun EXCEL-BUSY-P (xlsx / lockp)
  (setq lockp (EXCEL-LockFilePath xlsx))
  (not (null (findfile lockp))) ; sees hidden/system files too
)

  

Then this is the gatekeeper function.. 

 

;; IS THIS FILE BEING EDITED?
(if (EXCEL-BUSY-P file)
  (progn
    (princ (strcat
      "\n[FINDEXCELROW] Busy for editing (lock detected at "
      (EXCEL-LockFilePath file)
      "). Skipping to avoid AutoCAD freeze.\n"))
    (princ) ; clean return, no error
  )
  (progn
    ;; SAFE : PROCEED WITH READ
    (setq *excel-app* (vlax-get-or-create-object "Excel.Application"))
    (vla-put-visible *excel-app* :vlax-false)
    (setq *excel-workbooks* (vlax-get-property *excel-app* 'Workbooks))
    (setq *excel-openworkbook* (vlax-invoke-method *excel-workbooks* 'Open file 0 :vlax-true))
    (setq *excel-activesheet* (vlax-get-property *excel-openworkbook* 'ActiveSheet))

    ;; EXISTING SCAN LOOP / CLEANUP
  )
)

 

0 Likes
Message 16 of 17

DGCSCAD
Collaborator
Collaborator

Yes, that's a good way to go about that. I can work with this, thank you so much for all your help. I owe you a beer.

AutoCad 2018 (full)
Win 11 Pro
0 Likes
Message 17 of 17

5w1tch2
Explorer
Explorer

No problem! Best of luck