I am trying to convert Excel file to PDF using interop.excel, while executing ExportAsFixedFormat 'publishing' progress bar displays on the site. Is there any way to hide this? I found this issue for Excel files having size above 300KB.
Code is given below:
//(tried using Application instead of ApplicationClass)
Microsoft.Office.Interop.Excel.ApplicationClass excelApplication = new Microsoft.Office.Interop.Excel.ApplicationClass();
excelApplication.ScreenUpdating = false;
excelApplication.DisplayAlerts = false;
excelApplication.Visible = false;
if (excelWorkbook == null)
{
excelApplication.Quit();
excelApplication = null;
excelWorkbook = null;
return false;
}
var exportSuccessful = true;
try
{
excelApplication.AlertBeforeOverwriting = false;
excelWorkbook.ExportAsFixedFormat(Microsoft.Office.Interop.Excel.XlFixedFormatType.xlTypePDF, outputPath);
}
catch (System.Exception ex)
{
exportSuccessful = false;
}
I can't find any solution. My project is a C# web application.
It took me a few days to figure out, but finally here is a workarround which uses some WinAPI functions to observe windows events. While the hook is active, every new window is compared to whether its class is the same one as the PDF save dialog class. If that's the case, the window gets hidden.
Thanks for solution ideas go to some chinese guys:
http://www.itjie.wang/officebase/516998.html
Usage requirement:
OS (= Operating System) must be Windows because of WinAPI usage.
Warning:
If the "SetWinEventHook" isn't stopped again due to some errors, it is better to restart your system, otherwise you could run into some serious problems with Windows.
Note:
By default, the PDF save dialog doesn't appear regularly. It depends on the time necessary to save the PDF file. If it takes longer the save popup will show up. If it takes shorter the save popup won't show up. Anyway, you don't have to worry about whether the save dialog would appear or not, the code already does this for you.
Instruction:
In your Excel workbook, if you don't already have a module, create a new one (name doesn't matter) & paste following code:
' WINDOWS API FUNCTIONS:
Private Declare Function SetWinEventHook Lib "user32" (ByVal eventMin As Long, ByVal eventMax As Long, ByVal hmodWinEventProc As Long, ByVal pfnWinEventProc As Long, ByVal idProcess As Long, ByVal idThread As Long, ByVal dwFlags As Long) As Long
Private Declare Function UnhookWinEvent Lib "user32" (ByVal hWinEventHook As Long) As Long
Private Declare Function apiGetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As Long, ByVal lpClassname As String, ByVal nMaxCount As Long) As Long
Private Declare Function apiShowWindow Lib "user32" Alias "ShowWindow" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
' CONSTANT VARIABLES:
Private Const SW_HIDE = 0
Private Const DLG_CLSID = "CMsoProgressBarWindow"
Private Const EVENT_SYSTEM_FOREGROUND = &H3&
Private Const WINEVENT_OUTOFCONTEXT = 0
' GLOBAL VARIABLES:
Dim long_WinEventHook As Long
Public Function StartEventHook() As Long
long_WinEventHook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, 0&, AddressOf WinEventFunc, 0, 0, WINEVENT_OUTOFCONTEXT)
StartEventHook = long_WinEventHook
End Function
Public Sub StopEventHook()
Dim b_unhooked As Boolean
If long_WinEventHook = 0 Then
MsgBox "WinEventHook couldn't be stopped! " & _
"Variable 'long_WinEventHook' is empty! " & _
"Better restart Windows now!"
Exit Sub
End If
b_unhooked = UnhookWinEvent(long_WinEventHook)
If b_unhooked = True Then
Else
MsgBox "WinEventHook couldn't be stopped! " & _
"Variable 'b_unhooked' is false! " & _
"Better restart Windows now!"
End If
End Sub
' CALLBACK FUNC OF "SetWinEventHook" (DEFINE ACTIONS TO RUN ON THE EVENTS):
' http://stackoverflow.com/questions/20486944/detecting-in-vba-when-the-window-containing-an-excel-instance-becomes-active
Public Function WinEventFunc(ByVal HookHandle As Long, ByVal LEvent As Long, ByVal hWnd As Long, ByVal idObject As Long, ByVal idChild As Long, ByVal idEventThread As Long, ByVal dwmsEventTime As Long) As Long
'This function is a callback passed to the win32 api
'We CANNOT throw an error or break. Bad things will happen
On Error Resume Next
Dim l_handle As Long
Dim s_buffer As String
Dim b_visible As Boolean
Dim i_bufferLength As Integer
s_buffer = String$(32, 0)
i_bufferLength = apiGetClassName(hWnd, s_buffer, Len(s_buffer))
If Left(s_buffer, i_bufferLength) = DLG_CLSID Then
b_visible = apiShowWindow(hWnd, SW_HIDE)
WinEventFunc = hWnd
End If
End Function
In your VBA code, when you want to save your excel workbook as PDF, you would call above macros like this:
' ADD WINDOWS EVENT HOOK BEFORE SAVING:
Application.Run XL_WB.Name & "!StartEventHook"
' SAVE EXCEL AS PDF:
' https://msdn.microsoft.com/de-de/library/microsoft.office.tools.excel.worksheetbase.exportasfixedformat.aspx
XL_WB.ExportAsFixedFormat _
Type:=xlTypePDF, _
Filename:="C:\PDF.pdf", _
Quality:=xlQualityStandard, _
IncludeDocProperties:=True, _
IgnorePrintAreas:=False, _
OpenAfterPublish:=False
' REMOVE WINDOWS EVENT HOOK AFTER SAVING:
Application.Run XL_WB.Name & "!StopEventHook"
In the above VBA code example "XL_WB" is a variable. You have to adjust it to your needs. For example use "ActiveSheet" instead.
From following other websites users also asked for help with that particular problem:
https://social.msdn.microsoft.com/Forums/office/en-US/e6078904-0715-46a2-8937-c38626464425/exportasfixedformat-progress-bar-can-you-hide-it?forum=exceldev
http://www.vbaexpress.com/forum/archive/index.php/t-41431.html
http://www.excelbanter.com/showthread.php?t=446463
...
Related
I would like to set the volume of an specified process that is playing audio.
I found that I can do it with the waveOutSetVolume WinAPI function because it works as per-process, however, after I read these docs below I still can't figure out how to do it.
waveOutSetVolume function - MSDN
waveOutSetVolume function - pinvoke.net
I'm searching for a solution in C# or VB.Net.
This is the firm of my waveOutSetVolume:
<DllImport("winmm.dll", EntryPoint:="waveOutSetVolume", SetLastError:=True)>
Friend Shared Function WaveOutSetVolume(byval hwo As IntPtr,
byval dwVolume As UInteger) As Integer
End Function
And this is how I'm trying to call the function:
Public Sub SetVolume(ByVal hwo As IntPtr, ByVal volume As Integer)
If (volume < 0) OrElse (volume > 100) Then
Throw New ArgumentOutOfRangeException(
paramName:="volume",
message:="A value between 0 and 100 is required.")
Else
Dim valueFromPercent As UShort = CUShort(volume / (100US / UShort.MaxValue))
' Left channel volume
Dim loBytes As Byte() = BitConverter.GetBytes(valueFromPercent)
' Right channel volume
Dim hiBytes As Byte() = BitConverter.GetBytes(valueFromPercent)
Dim dWord As UInteger =
BitConverter.ToUInt32(loBytes.Concat(hiBytes).ToArray, 0)
NativeMethods.WaveOutSetVolume(hwo, dWord)
End If
End Sub
I know that an hwo will not be the same as a process handle, but as I said I can't figure it out how to discover the hwo of a process.
Note my DWORD calculation seems not be the problem because if I pass a Intptr.Zero to the function then it sets the expected volume for the current application.
I'm trying to automatize some Excel reports. Currently I need to retrieve some data from an Essbase Server, in order to achieve this I've created a macro to retrieve and set data in an Excel sheet, my VBA code is the following:
Option Explicit
Declare Function EssVRetrieve Lib "ESSEXCLN.XLL" (ByVal sheetName As Variant, ByVal range As Variant, ByVal lockflag As Variant) As Long
Declare Function EssVConnect Lib "ESSEXCLN.XLL" (ByVal sheetName As Variant, ByVal userName As Variant, ByVal password As Variant, ByVal server As Variant, ByVal application As Variant, ByVal database As Variant) As Long
Declare Function EssVDisconnect Lib "ESSEXCLN.XLL" (ByVal sheetName As Variant) As Long
Sub Essbase_Update_Pulls()
Dim rangeString As String
rangeString = "B3:AC5033"
MsgBox ("Starting macro")
Dim wbSrc As Workbook
Dim m As Variant
Dim mySheetname As Variant, myUserName As Variant, myPassword As Variant, myServer, myApp As Variant, myDB As Variant
Dim lockflag As Integer
Dim myrng As range
Dim x As Integer
Dim y As Integer
Dim z As Integer
Dim strMsgTxt As String
Dim blnRetVal As Boolean
Set wbSrc = ActiveWorkbook
Set myrng = range(rangeString)
lockflag = 1
MsgBox ("Data set")
mySheetname = "Sheet"
myServer = "Server"
myApp = "App"
myDB = "DB"
myUserName = "User"
myPassword = "Pass"
MsgBox ("Trying connection")
x = EssVConnect(mySheetname, myUserName, myPassword, myServer, myApp, myDB)
MsgBox (CStr(x))
If x < 0 Then
blnRetVal = False
strMsgTxt = "Essbase Login - Local Failure"
MsgBox (strMsgTxt)
ElseIf x > 0 Then
blnRetVal = False
strMsgTxt = "Essbase Login - Server Failure"
MsgBox (strMsgTxt)
Else
blnRetVal = True
strMsgTxt = "Success"
MsgBox ("Connection Succeeded")
y = EssVRetrieve(mySheetname, myrng, lockflag)
If y = 0 Then
MsgBox ("Retrieve successful.")
z = EssVDisconnect(mySheetname)
If z = 0 Then
MsgBox ("Disconnect Succeed.")
Else
MsgBox ("Disconnect failed.")
End If
Else
MsgBox ("Retrieve failed.")
End If
End If
End Sub
Variable x is supposed to return the status code (0 is success any other is failed).
So here comes the trick, whenever I run this macro within Excel it runs perfectly, however when I call it from C# using xlApp.Run("Essbase_Update_Pulls"); it returns a status code of -3.
Doing some research I found out that whenever an Excel Application is created in code it doesn't have the add-ins loaded, so they have to be manually loaded
https://community.oracle.com/thread/2480398 .
I iterated over the xlApp.AddIns and found that the "essexcln.xll" was correctly installed so I have no idea what to do now. Also I found out that Add-Ins can be added during runtime but this just causes an exception, here is the source:
http://www.network54.com/Forum/58296/thread/957392331/Visual+Basic-Excel+Api+call+to+Essbase
Found out that excel isn't loading all the dll's and xll's required to connect to de Essbase server. In order to make it work it is necessary to start excel as a process and the acquire the instance and relate it to the interop class. I found the solution here:Excel interop loading XLLs and DLLs. The user pretty much had the same problem but with Bloomberg. I would just add that in the SearchExcelInterop method it needs a Thread.Sleep() to wait for Excel to load properly, in my case it threw a StackOverflowException.
I have a annoying problem.
I am trying to delete a registry value (no My Namespace) using the RegDeleteKey Value API, but with one modification.
I want the function have an absolute path as parameter.
So something like this:
RegDeleteKeyValue("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run","myValue")
Atm it looks like this (This one works, but has no absolute path as parameter, like it should be):
Private Enum RegHive
HKEY_CLASSES_ROOT = &H80000000
HKEY_CURRENT_USER = &H80000001
HKEY_LOCAL_MACHINE = &H80000002
HKEY_USERS = &H80000003
HKEY_CURRENT_CONFIG = &H80000005
End Enum
RegDeleteKeyValue(Reghive.HKEY_CURRENT_USER,"Software\Microsoft\Windows\CurrentVersion\Run","Myvalue")
I tried it to modify the function like this, so the parameters will only be the absolute path and the registryvalue.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
DeleteReyKeyValue("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run", "123")
End Sub
Public Shared Function DeleteReyKeyValue(ByVal Key As String, ByVal valueName As String) As Long
Dim Handle As IntPtr
Dim Hive As String = Split(Key, "\")(0)
Select Case Hive
Case "HKEY_CLASSES_ROOT"
Handle = CType(&H80000000, IntPtr)
Case "HKEY_CURRENT_USER"
Handle = CType(&H80000001, IntPtr)
Case "HKEY_LOCAL_MACHINE"
Handle = CType(&H80000002, IntPtr)
Case "HKEY_USERS"
Handle = CType(&H80000003, IntPtr)
Case "HKEY_CURRENT_CONFIG"
Handle = CType(&H80000005, IntPtr)
End Select
Key = Key.Replace(Hive, String.Empty)
Return RegDeleteKeyValue(Handle, Key, valueName)
End Function
<DllImport("advapi32.dll")> _
Private Shared Function RegDeleteKeyValue(ByVal handle As IntPtr, ByVal keyName As String, ByVal valueName As String) As Long
End Function
When using this, nothing happens, no error, but the Registryvalue gets not deleted? ;O
Why is that?
The question is already answered in stackoverflow
So to delete your value, you should use this code,
string keyName = #"Software\Microsoft\Windows\CurrentVersion\Run";
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(keyName, true))
{
if (key == null)
{
// Key doesn't exist. Do whatever you want to handle
// this case
}
else
{
key.DeleteValue("123");
}
}
This would delete the value from registry. I am not sure why you are adding such complex codes.
Here is a link to msdn : http://social.msdn.microsoft.com/Forums/vstudio/en-US/5b22e94c-37a9-4be5-ad55-3d9229220194/how-to-use-add-read-change-delete-registry-keys-with-vbnet?forum=vbgeneral
It has the codes for VB but still it is nearly the same for c#, you can use a online converter to convert the codes, http://converter.telerik.com/
Thanks for your quick reply.
In that case a friend helped me out, code is now working.
I was only missing a + "\"
This works now:
Public Shared Function DeleteReyKeyValue(ByVal Key As String, ByVal valueName As String) As Long
Dim Handle As IntPtr
Dim Hive As String = Split(Key, "\")(0)
MsgBox("Hive:" & Hive)
Select Case Hive
Case "HKEY_CLASSES_ROOT"
Handle = CType(&H80000000, IntPtr)
Case "HKEY_CURRENT_USER"
Handle = CType(&H80000001, IntPtr)
Case "HKEY_LOCAL_MACHINE"
Handle = CType(&H80000002, IntPtr)
Case "HKEY_USERS"
Handle = CType(&H80000003, IntPtr)
Case "HKEY_CURRENT_CONFIG"
Handle = CType(&H80000005, IntPtr)
End Select
Key = Key.Replace(Hive + "\", String.Empty)
Return RegDeleteKeyValue(Handle, Key, valueName)
End Function
<DllImport("advapi32.dll")> _
Private Shared Function RegDeleteKeyValue(ByVal handle As IntPtr, ByVal keyName As String, ByVal valueName As String) As Long
End Function
Credits: Rajin
I need copy a file from server to another server using asp.net/VB.NET for example:
'We copy the original file to the Temp dir
If File.Exists(sFileSource) Then
File.Copy(sFileSource, sFileDest, True)
Else
'The file doesn't exist
End If
But when validate if file exists get that the file doesn't exist, I saw another forum that is a problem of network credentials because the paths are way //192.168.1.10/TemFile/file01.txt.
I have the answer, using a system dll alow connect to oter servers by NetBios
<DllImport("advapi32.DLL", SetLastError:=True)> _
Public Shared Function LogonUser(ByVal lpszUsername As String, ByVal lpszDomain As String, _
ByVal lpszPassword As String, ByVal dwLogonType As Integer, ByVal dwLogonProvider As Integer, _
ByRef phToken As IntPtr) As Integer
End Function
Private _adminToken As IntPtr
Private _widCurrent As WindowsIdentity
Private _widAdmin As WindowsIdentity
Private _wic As WindowsImpersonationContext
Private _admin As AdminShared.AdminManager
If LogonUser(sUser, sDomain, sPassword, 9, 0, _adminToken) <> 0 Then
_widAdmin = New WindowsIdentity(_adminToken)
_wic = _widAdmin.Impersonate()
File.Copy(sFileSource, sFileDest)
Else
I am kind of stuck with implementing the important feature which requires data to be cleared from office clipboard the moment it is copied.
The intention is not to share the contents with other office programs e.g. word, powerpoint etc. the scenario is that I have some important content in my excel sheet. Once I make a copy of it, it is soon available on office clipboard. if I keep copying the stuff in excel, it keeps collecting in other office programs. However, the windows clipboard would contain only the recent enter which can be cleared using
System.Windows.Forms.Clipboard.clear():
Is there a way out to clear the office clipboard to?
I googled and found out that there may not be a clear cut solution to the program but getting office clipboard window with the help of FindWindowEx(....) should be possible and then message to can be sent in order to clear the contents. It seems that I am not able to get it this way.
can someone tell if they have experienced the same problem?
This might give you a nudge in the right direction... Taken From: mrexcel.com
Option Explicit
Sub myClr()
'Put this sub inta a Sheet Module, like: Sheet1.
Call ClearOfficeClipboard
End Sub
'Put the code from here down into a Standard Module, like Module1.
Private Declare Function FindWindowEx Lib "user32.dll" _
Alias "FindWindowExA" (ByVal hWnd1 As Long, _
ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function PostMessage Lib "user32.dll" Alias _
"PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Private Const WM_LBUTTONDOWN As Long = &H201&
Private Const WM_LBUTTONUP As Long = &H202&
'creates a long variable out of two words
Private Function MakeLong(ByVal nLoWord As Integer, ByVal nHiWord As Integer) As Long
MakeLong = nHiWord * 65536 + nLoWord
End Function
Sub ClearOfficeClipboard()
Dim hMain&, hExcel2&, hWindow&, hParent&
Static sTask As String
'****Dim hClip As Long************************'changed by Lary
Dim octl, bScreenUpdatingIsOn As Boolean
Static lParameter As Long, bNotFirstVisibleTime As Boolean, hClip As Long, bNotFirstTime As Boolean
If Not (bNotFirstTime) Then
lParameter = MakeLong(120, 18)
sTask = Application.CommandBars("Task Pane").NameLocal
'Handle for XLMAIN
hMain = Application.hwnd
bNotFirstTime = True
End If
With Application.CommandBars("Task Pane")
If Not .Visible Then
'assume have to force the window if it is not visible, since it appears that
' the window class does not remain loaded if you clear a non-visible clipboard
'determine current status of screenupdating so that this sub does not change it
bScreenUpdatingIsOn = Application.ScreenUpdating
If bScreenUpdatingIsOn Then Application.ScreenUpdating = False
Set octl = Application.CommandBars(1).FindControl(ID:=809, recursive:=True)
If Not octl Is Nothing Then octl.Execute
.Visible = False
'return to screenupdating on if that is what it was in the beginning
If bScreenUpdatingIsOn Then Application.ScreenUpdating = True
If hClip = 0 Then
hParent = hMain: hWindow = 0
hWindow = FindWindowEx(hParent, hWindow, "MsoWorkPane", vbNullString)
If hWindow Then
hParent = hWindow: hWindow = 0
hClip = FindWindowEx(hParent, hWindow, "bosa_sdm_XL9", vbNullString)
End If
End If
Else
If Not (bNotFirstVisibleTime) Then** 'find hClip if window is visible
Do
hExcel2 = FindWindowEx(hMain, hExcel2, "EXCEL2", vbNullString)
hParent = hExcel2: hWindow = 0
hWindow = FindWindowEx(hParent, hWindow, "MsoCommandBar", sTask)
If hWindow Then
hParent = hWindow: hWindow = 0
hWindow = FindWindowEx(hParent, hWindow, "MsoWorkPane", vbNullString)
If hWindow Then
hParent = hWindow: hWindow = 0
hClip = FindWindowEx(hParent, hWindow, "bosa_sdm_XL9", vbNullString)
If hClip > 0 Then
Exit Do
End If
End If
End If
Loop While hExcel2 > 0
bNotFirstVisibleTime = True
End If
End If
End With
If hClip = 0 Then
MsgBox "Cant find Clipboard window"
Exit Sub
End If
Call PostMessage(hClip, WM_LBUTTONDOWN, 0&, lParameter)
Call PostMessage(hClip, WM_LBUTTONUP, 0&, lParameter)
End Sub
The code below has been customized for Excel 2013 in vb.net. Just add a button to your ribbon and code works like a charm.
Private Const WM_LBUTTONDOWN As Long = &H201&
Private Const WM_LBUTTONUP As Long = &H202&
WithEvents oAppWD As Excel.Application
Public oDoc As Excel.Workbook
Private Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Int32)
Private Declare Function FindWindowEx Lib "user32.dll" Alias "FindWindowExA" (ByVal hWnd1 As Int32, ByVal hWnd2 As Int32, ByVal lpsz1 As String, ByVal lpsz2 As String) As Int32
Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Int32
Private Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hWnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As Int32) As Int32
Declare Function BringWindowToTop Lib "user32" (ByVal hWnd As Int32) As Int32
Private Sub Button1_Click(sender As Object, e As RibbonControlEventArgs) Handles Button1.Click
Dim hMain As Int32, hWord As Int32, hClip As Int32, hWindow As Int32, hParent As Int32
Dim lParameter As Int32
Dim sTask As String
Dim HWND As Int32
'Open the selected File
oAppWD = Globals.ThisAddIn.Application 'DirectCast(System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application"), Excel.Application)
oAppWD.Visible = True
oDoc = oAppWD.ActiveWorkbook
oDoc.Activate()
oDoc.Windows(1).Activate()
Sleep(2000)
' MessageBox.Show("Doing it.....")
HWND = FindWindow("XLMAIN", vbNullString)
' Make Office Clipboard Visible
oAppWD.CommandBars("Office Clipboard").Visible = True
BringWindowToTop(HWND)
' Get the handles of the respective Windows Of the Office
sTask = "Office Clipboard"
hMain = HWND
hWord = FindWindowEx(hMain, 0, "EXCEL2", vbNullString)
hParent = hWord : hWindow = 0
hWindow = FindWindowEx(hParent, 0, "MsoCommandBar", sTask)
If hWindow Then
hParent = hWindow : hWindow = 0
hWindow = FindWindowEx(hParent, 0, "MsoWorkPane", vbNullString)
If hWindow Then
hParent = hWindow : hWindow = 0
hClip = FindWindowEx(hParent, 0, vbNullString, "Collect and Paste 2.0")
End If
End If
If hClip = 0 Then
MsgBox("Cant find Clipboard window")
Exit Sub
End If
' Pass the message 120,18 are the respective co-ordinates of the Clear all button.
lParameter = MakeLong(120, 18)
' Send the Message
Call PostMessage(hClip, WM_LBUTTONDOWN, 0&, lParameter)
Call PostMessage(hClip, WM_LBUTTONUP, 0&, lParameter)
Sleep(100)
End Sub
Private Function MakeLong(ByVal nLoWord As Integer, ByVal nHiWord As Integer) As Int32
MakeLong = nHiWord * 65536 + nLoWord
End Function