簡體   English   中英

在活動 ZBF57C906FA7D2BB656D07372E451 文檔中使用 VBA 從 powershell 連接到 Excel

[英]Connecting to Excel from powershell using VBA in an active excel document

我有一個非常奇怪的問題。

我有一些名為 merge1-6 的 vba 腳本,它從不同的 excel 表中提取數據。 這 6 個生成的文本文件使用另一個宏“MergeALL”合並為 1 個文本文件,因此所有內容都在一個字符串上。

到目前為止一切順利,效果很好。

所以我想制作一個 powershell 腳本來訪問我打開的當前 excel 文檔,並獲取 J29 單元格數據,並將此數據與值“0|”合並組合給定的字符串。

實際powershell腳本我要拉取單元格數據如下:

宏:調用 BID_MergeXCleanup_TextOut

#This script opens H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.txt and replace empty parts of "domain/users/0|" with "nothing"
#stripping away all entries not needed.  Out-File used as output, with -NoNewline to prevent carriage return inserting empty line.
#$workbook = $excel.Workbooks.Open("H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.xlsm") #open workbook

$xl = [Runtime.InteropServices.Marshal]::GetActiveObject(Excel.Application')
$workbook = $xl.workbooks | ?{$_.FullName -eq "H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.xlsm"}
$workbook.sheets.item(1).activate() #opens the sheet and activates it
$WorkbookTotal=$workbook.Worksheets.item(1) #Set sheet data to variable
$value = $WorkbookTotal.Cells.Item(29, 10)  # Fetches data in Column 10, Row 29 which is the cell reference J29 where domain is. 
$domain = $value.Text  #Fetches "domain/users/" from the Fetch-tab of the excel file, to match the correct domain in use and stores result in the $domain variable
$leftoverdomain = '0|'    #last part of domain string after the domain/user part
$joinedvariables = -join($domain,$leftoverdomain,"") #Joines the above variables together to form "domain/users/0|" as searchtag to replace as leftover bits.

$b = Get-Content -Path "H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.txt" #Opens text file to replace in.

#Next part uses the $joinedvariables variable of merged search parametres and replaces it with "nothing" and saves the file back.

@(ForEach ($a in $b) {$a.Replace($joinedvariables, '')}) | Out-File "H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.txt" -NoNewline
Start-Sleep -Seconds 0.3

### INFO ###
#I used a reference to the EXCEL cell here, instead of directly from the TXT-file as given what domain has been specified, the info in the TEXT file would vary
#But text in J29 would always equal the domain being used, and thus also correnspond with the one in the text file at all times. 
#This way it will automatically always use the correct domain.

因此,如果我使用對 excel 文檔的獨立引用,

$workbook = $excel.Workbooks.Open("H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.xlsm") 

...並直接打開工作簿,在 powershell ISE 中,命令成功。 但是,如果我調用 PS1 腳本或從 excel 文件中手動運行它,則無法正確執行。 這就是為什么我開始尋找如何訪問當前的 excel session 引導我到下一個方法...

如果我使用

$xl = [Runtime.InteropServices.Marshal]::GetActiveObject('Excel.Application') 

要引用當前正在運行的應用程序,當我手動運行時,它從 ISE 和 Excel 內部都成功。

在這兩種情況下,只要要修改的 TXT 文件存在並且其中包含要搜索的信息,這項工作就有效。

因此,鑒於我有一個“未修改的文件”,我想通過從另一個宏調用它們來啟動這些宏。

這是 VBA 代碼,用於在同一 excel 表中啟動宏。

Call BID_1_TextOut 
    Application.Wait Now + TimeSerial(0, 0, 1)
Call BID_2_TextOut 
    Application.Wait Now + TimeSerial(0, 0, 1)
Call BID_3_TextOut 
    Application.Wait Now + TimeSerial(0, 0, 1)
Call BID_4_TextOut 
    Application.Wait Now + TimeSerial(0, 0, 1)
Call BID_5_TextOut 
    Application.Wait Now + TimeSerial(0, 0, 1)
Call BID_6_TextOut 
    Application.Wait Now + TimeSerial(0, 0, 1)
Call BID_MergeALL_TextOut 'Macro MergeALL - Merges all BID-TEXTOUT files to 1 file
    Application.Wait Now + TimeSerial(0, 0, 3)
Call BID_MergeXCleanup_TextOut 'Macro MergeXCleanup - This script replace empty parts of "domain/users/0|" with "nothing"
    Application.Wait Now + TimeSerial(0, 0, 1)

最后兩個宏是重要的。 第二個最后一個宏准備要在“BID_MergeXCleanup”中使用的文件,這是我正在調用的 PS1 腳本。

我調用的宏帶有以下代碼:

' Script to merge all the BID-TEXT-OUT files into 1 string with UTF32LE encoding.

Sub BID_MergeALL_TextOut()

Set objShell = CreateObject("wscript.Shell")
objShell.Run ("powershell.exe H:\Temp\PublicFolder-powershell\MergeALL_UTF32.ps1")

End Sub

'This script opens H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.txt and replace empty parts of "domain/users/0|" with "nothing"
'stripping away all entries not needed.

Sub BID_MergeXCleanup_TextOut()

Set objShell = CreateObject("wscript.Shell")
objShell.Run ("powershell.exe -noexit H:\Temp\PublicFolder-powershell\REPLACE_IN_EmptyDomainUsersPart.ps1")

End Sub

奇怪的是,如果我使用“調用腳本”自動連續運行這些命令,最后一個腳本總是失敗

“不能調用方法或空值表達式。

如果我之后檢查 TXT 文件,我會看到只有'0|' 變量已被刪除,因為它似乎無法從 Excel 文檔中獲取 J29 信息。

如果我從 Excel中手動逐個運行每個宏,那么它們會正確執行,並且腳本會按照我的意願執行,不會出現錯誤

merge1-6-then-all-then-cleanup

所以據我所知,我的 powershell 腳本能夠調用活動的 excel 工作簿、工作表、單元格、獲取數據等等。

...但是我不能通過從另一個宏調用宏來做到這一點? 在此示例中,如果我使用“Call BID_MergeXCleanup_TextOut”引用來訪問宏,它將失敗。

PS:我也嘗試“共享”工作簿,以防是權限問題,但運行 Call-VBA 腳本時仍然報錯,但沒有連續手動運行每個宏。

知道如何解決這個問題嗎?

我有一種感覺,我可能不允許原始 PS1 文件退出 $NULL 表達式,但我不確定如何添加它。

第一個失敗的代碼行是

$workbook.sheets.item(1).activate() 

我試圖在 Powershell ISE 中添加 $NULL ,然后它失敗了

$null = $workbook.sheets.item(1).activate()     <<<--- Fails with *"You cannot call a method on a null-valued expression."*

而且我也看不到主腳本中有任何未清除的變量。

不確定如何解決這個問題。

我最終得到了什么:

所以@postanote在這里建議我使用COM而不是InteropService.Marshal來訪問Excel。

我最初認為這行不通,因為它會打開工作簿的一個新實例,但是 COM 版本有一種隱藏您運行的實例的方法

$objExcel.Visible = $false

現在,[Runtime.InteropServices.Marshal]::GetActiveObject 版本也有這個值,它似乎有所不同,因為如果我將代碼更改為 COM,如下所示:

$objExcel = New-Object -ComObject Excel.Application
$objExcel.Visible = $false
$WorkBook = $objExcel.Workbooks.Open("H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.xlsm")
$WorkSheet = $WorkBook.sheets.item("Fetch")
$domain = $worksheet.Rows.Item(29).Columns.Item(10).Text
$leftoverdomain = '0|'    #last part of domain string after the domain/user part
$joinedvariables = -join($domain,$leftoverdomain,"") #Joines the above variables together to form "domain/users/0|" as searchtag to replace as leftover bits.

$b = Get-Content -Path "H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.txt" #Opens text file to replace in.

@(ForEach ($a in $b) {$a.Replace($joinedvariables, '')}) | Out-File "H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.txt" -NoNewline
Start-Sleep -Seconds 0.3

...當從 VBA 調用腳本調用時,腳本執行沒有錯誤...

另一方面,如果我嘗試將不可見成員合並到原始代碼中,如下所示:

$xl = [Runtime.InteropServices.Marshal]::GetActiveObject('Excel.Application') #opens an already open excel session
$xl.Visible = $false
$workbook = $xl.workbooks | ?{$_.FullName -eq "H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.xlsm"} 
$workbook.sheets.item(1).activate() 
$WorkbookTotal=$workbook.Worksheets.item(1) 
$value = $WorkbookTotal.Cells.Item(29, 10)  
$domain = $value.Text  
$leftoverdomain = '0|'   
$joinedvariables = -join($domain,$leftoverdomain,"") 

$b = Get-Content -Path "H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.txt" 

@(ForEach ($a in $b) {$a.Replace($joinedvariables, '')}) | Out-File "H:\Temp\PublicFolder-powershell\Create-String-For-Email-Fetch.txt" -NoNewline
Start-Sleep -Seconds 0.3

...然后它失敗並顯示以下更改的錯誤消息:

Call was rejected by callee. (Exception from HRESULT: 0x80010001 (RPC_E_CALL_REJECTED))

rpc 拒絕錯誤信息

所以我想最終的結果是,只要你從 Windows window 處理程序中隱藏新的 excel 實例,COMs 就可以多次打開同一個文檔。

但是......很高興知道為什么原始代碼在手動運行時有效,但在從腳本調用時無效,所以如果有人知道,我很想知道那個答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM