I have created Drag-n-Drop form for Excel in order to capture link to file location using treeview control (code bellow). It works as intended, however problem that after I make form ShowModal = False (because user might want to move Excel window in order to reach file-to-be-dragged) after it runs it's routine, error message pops-up informing that "File format is not valid" (screen below) or notification that file might be corrupt or unsafe (second screen below).
To my understanding that happens because Excel considers file to be dropped on the sheet and tries to open it (it will be most likely .pdf file).
Is there a way to prevent that other than making form Modal? To my understanding to achieve that somehow error message should be prevented or Excel should not try to open file at all and by doing that avoid message altogether (best case).
Code for Drag-n-Drop functionality:
Private Sub TreeView1_OLEDragDrop(Data As MSComctlLib.DataObject, Effect As Long, Button As Integer, Shift As Integer, x As Single, y As Single)
'for capturing draged file path
'VBA does not have normal native functionality to do that so it is solved by using treeview widget unconventionaly
Dim LinkToPass As String
LinkToPass = Data.Files(1)
MsgBox "Thank you! Link captured.", vbInformation, "Link captured"
'Pass information to another form, where user enters all other data required
If formLoaded("NewEntry_agreement") Then
NewEntry_agreement.LinkToFile.Caption = LinkToPass
End If
CloseBtt_Click 'just call close button Sub with Unload Me inside
End Sub
EDIT: Additional info and screenshot about alternative message. Also made goal more clear - either prevent message or prevent Excel from trying to open the file and by doing that prevent error message.
Click the form to toggle modal/modeless
' Adapted from Stephen Bullen's ModelessForm.xls 1998 example
Private Declare PtrSafe Function EnableWindow Lib "user32" (ByVal hwnd As LongPtr, ByVal fEnable As Long) As Long
' click the form to toggle modal/modeless
Private Sub UserForm_Click()
Static lMode As Long
lMode = IIf(lMode = 0, 1, 0)
EnableWindow Application.hwnd, lMode
Me.Caption = IIf(lMode, "Modeless", "Modal")
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
' ensure the app window is reset (maybe trap the state and reset if/as necessary)
EnableWindow Application.hwnd, 1
End Sub
I understand what you describe but I don't think there's a simple solution. Easiest would be if this works -
Call DragAcceptFiles(FindWindow("ThunderDFrame", Me.Caption), 0)
.. but unfortunately it doesn't. Neither does does attempting to disable the xlApp.Hwnd the same way from accepting dropped files. Maybe need to disable one of the other bunch of windows(?), I've only tried the ones I mentioned.
Briefly two different approaches you might look into -
Add a button for the user to toggle modeless / modal before enabling drag files operation. I don't have the code to hand, but it's definitely possible albeit not supported.
Instead of the treeview's OLE-DD setup a callback with CallWindowProc to trap the WM_DROPFILES message, get the files with DragQueryFile, and prevent Excel from receiving the message with DragFinish. You'd need a window and this could be the form's first and only direct Child window. Better though to add a window'd control such as a Frame (though it doesn't directly expose its 'hwnd' so a fair bit of API work to get it). Plenty of general examples out there and I've had this approach working - but unfortunately there are several catches and I don't have anything reliable enough I'd want to post!
This won't be the answer you're looking for but it might be the best you're going to get! Though I'd be pleased to be wrong:)
I want to detect when user clicks File >> Print or CTRL+P inside ms word or excel and use this detection to run a batch file using vba code, is this possible?
This code should self start along with the program.
I tried to find similar code but was unable to find anything useful to my need.
Any help would greatly appreciated.
Thanks
The way your question is written seems ambiguous to me. At first read, it seems like you are trying to distinguish between these two methods of telling a file to print. I know of no way to do this in vba.
However, you can intercept the print event or command.
Another possible meaning is that you want your procedure to run whenever the user attempts to print. See Intercepting Events Like Save or Print by Word MVPs Dave Rado and Jonathon West. See also Application.WorkbookBeforePrint Event.
Note, this does not block screenshots or saving to another file. Do you mind sharing why you are trying to do this? What you hope to accomplish?
You can use the DocumentBeforePrint and WorkbookBeforePrint Events. Below quoted from linked pages on Intercepting Events and WorkBookBeforePrint documentation.
A DocumentBeforePrint event procedure looks like this:
Private Sub oApp_DocumentBeforePrint(ByVal Doc As Document, _
Cancel As Boolean)
'Your code here
End Sub
If you want to prevent printing from occurring in certain
circumstances, you can set the Cancel variable to True, e.g.:
Private Sub oApp_DocumentBeforePrint(ByVal Doc As Document, _
Cancel As Boolean)
Dim Result As Long
Result = MsgBox("Have you checked the " & "printer for letterhead paper?", vbYesNo)
If Result = vbNo Then Cancel = True
End Sub
From Excel documentation
This example recalculates all worksheets in the workbook before
printing anything.
Private Sub App_WorkbookBeforePrint(ByVal Wb As Workbook, _
Cancel As Boolean)
For Each wk in Wb.Worksheets
wk.Calculate
Next
End Sub
End Quoted Material
Intercepting the Command instead of the Event
Another, less effective, method is to Intercept the actual commands. You could name your procedure PrintPreviewAndPrint and have another called FilePrintQuick that calls your procedure PrintPreviewAndPrint. Earlier versions use FilePrint and FilePrintDefault. Thank #Timothy Rylatt for the command names. He adds: Note that neither of these will intercept the backstage command accessed via File | Print. For that you need to use an event.
Sub PrintPreviewAndPrint()
' Your code here
End Sub
Sub FileQuick()
FilePreviewAndPrint
End Sub
In Word, these would go in your template or in a Global Template.
In Word, you make a template a Global Template by placing it in your Word Startup Folder.
Dealing with making this Global in Excel
My understanding of how Excel handles global macros is far poorer than that for Word. To assist with this, I asked my own question in the Microsoft Answers Excel Programming forum. Here is a link to that question and the answers I received. Andreas Klinger, who is engaged in that thread, is an experienced and knowledgeable Excel programmer, which I am not.
I am having the following problem: my client updated Excel version and all user forms appear to be broken. (Described here: Excel UserForm displays at the wrong size)
There is a special default setting "Optimize for best appearance" (When using multiple displays) in newer Excel versions that causes the problem. When I switch to "Optimize for compatibility" option, the problem is gone.
Question: did someone figure out how to change this setting programmatically (in VBA code)?
I tried to record the macro and change the settings but no luck. The resulting program is empty.
I did not manage to find a workaround. Apparently when the "optimise for best appearance" option is active and multiple monitors are connected to a device, excel cannot properly build buttons, objects etc. Telling each user not to use multiple monitors or to switch this option manually off is not an option.
I am changing right now the application in the way that I do not use any objects. It sucks but there is no other way.
Well you could try to change the registry settings:
HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Excel\Options\RenderForMonitorDpi
Doing something like (Source):
Public Function RegWrite(ByVal Path As String, _
ByVal Value As String, _
Optional ByVal Typ As String = "REG_SZ") As Boolean
Dim ws As Object
On Error Goto ErrHandler
Set ws = CreateObject("WScript.Shell")
ws.RegWrite Path, Value, Typ
RegWrite = True
Exit Function
ErrHandler:
RegWrite = False
End Function
However it did not work for me. It changes the registry value but after the restart excel won't change the setting and will change the registry value back to the old value.
Well, I suppose there is always SendKeys to change the display option. :(
There was a time when COM/VBA was important and all options have Application.* properties, but sadly not any more.
In order to head off a storm of "comment it out" replies, here is my situation:
I have a process is normally run 1 iteration by 1 iteration. A user manually hits a button that calls a macro which, upon completion, pops up a message box that reports the total time length the macro ran for. It's pretty handy for diagnosing issues. This code is locked down and I cannot modify it.
I am trying to do this at scale. Because the code in the main spreadsheet and workbook are locked, I have a separate workbook open in the same instance of excel with a macro that operates the locked down workbook. Rather than 1 by 1, I've got a set of 300 I'm trying to run through. Right now I have to babysit the thing and hit space to get past the MsgBox. Does anyone know of any tricks to prevent me having to monitor the thing? Either disabling the pop-ups or some way to make them non-modal. Maybe a trick to make the mouse click?
You're right in knowing that the best way to fix the issue is to correct the code. In which case you would probably make the pop-ups toggle-able.
However, I wrote this for you which could be used as a potential work around. It utilizes VBScript to "sort-of" simulate multithreading so that you can send a key to the modal Msgbox. Assuming you can do what you want to do via code, simply call SendDelayedKeys before the action that will cause a Msgbox. You may have to tinker with the Delay based upon your circumstances as 100 milliseconds may not be enough. To change the Delay, just call like this: SendDelayedKeys 500 for 500 milliseconds.
Sub SendDelayedKeys(Optional Delay As Long = 100, Optional keys As String = """ """)
Dim oFSO As Object
Dim oFile As Object
Dim sFile As String
sFile = "C:\SendKeys.vbs" 'Make this a valid path to which you can write.
'Check for the .vbs file.
If Not Len(Dir$(sFile)) Then
'Create the vbs file.
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFile = oFSO.CreateTextFile(sFile)
oFile.WriteLine "Set WshShell = WScript.CreateObject(""WScript.Shell"")"
oFile.WriteLine "WScript.Sleep CLng(WScript.Arguments(0))"
oFile.WriteLine "WshShell.SendKeys WScript.Arguments(1)"
oFile.Close
End If
Shell "wscript C:\SendKeys.vbs " & Delay & " " & keys
End Sub
Sub ProofOfConcept()
'Using default parameters which sends a space after 100 milliseconds
SendDelayedKeys
MsgBox "I disappear on my own!"
End Sub
A word of warning: Any solution that utilizes SendKeys is a fragile solution and should be avoided when possible. However, when your options are limited and you need to avoid a manual process, sometimes it's your only option.
Since SiddhartRout rightly pointed out that this could be solved using API calls: here's a link with C# code that would close your msgbox every second.
The problem here really isn't strictly a problem more code can (or indeed should) solve.
There are a great many things to consider and any solution will be more complex AND less reliable than the problem it is initially trying to solve. But lets look at your options...
SendKeys is not reliable for that kind of use, what happens if the dialogue says "would you like me to save this workbook?" just after making a change that was meant to be temporary or "would you like to play global thermonuclear war?" Plus with a batch process like that you want to get on with something else while you wait, even if it's only to come here to downvote trolls. If nothing else you may not be in control of this code so what kind of mess will it cause when the maintainers realise msgbox is bad UX and kill it?
FindWindow API calls would let you check the content in the window to make sure it says what you're expecting but then you're potentially asking some bit of quick & dirty vbscript to go into a race condition until the right window comes up. Can you guarantee that the threads won't lock up?. What about platform issues - what happens if someone wants to run your code on their shiny new surface? What happens when your 64 bit modal Excel dialogue window can't be seen by the 32-bit api calls you were making? What about a new version of office that doesn't present modal dialogues in the same way? None of those problems are insurmountable but each adds complexity (aka opportunity for failure.)
The best solution is fix the actual problem you have identified from the outset which is that the original code throws up an unnecessary modal dialogue. Someone needs to fix that - it doesn't have to be you but if you estimate how much time that modal dialogue wastes in lost productivity that should get you a solid business case for getting it sorted.
I have a simple inputbox in my VBA code. I would like to set its starting position. I know the parameters [LEFT] and [TOP] should do that, but they won't work.
Here is what I have:
x = Application.InputBox(MyPrompt, MyTitle, , 50, 50)
and here is the function syntax
InputBox(Prompt, Title, Default, Left, Top, HelpFile, HelpContextId, Type)
(I left the third parameter [DEFAULT] blank).
No matter what numbers I use for LEFT and TOP, the inputbox always starts at the same place.
What is wrong with that?
There's a difference between
InputBox("prompt","title","default", 10000,10000)
which does use the top/left parameters, and
Application.InputBox("prompt","title","default", 10000,10000)
which doesn't.
Note the top/left are in Points, so your provided values will need to be larger than you may be used to...
http://msdn.microsoft.com/en-us/library/office/aa195768(v=office.11).aspx
"The InputBox method differs from the InputBox function in that it allows selective validation of the user's input, and it can be used with Microsoft Excel objects, error values, and formulas. Note that Application.InputBox calls the InputBox method; InputBox with no object qualifier calls the InputBox function."
High,
This Thread came up a lot in searches I did a couple of weeks ago when trying to resolve a similar issue.
I have a solution now, so thought I would pass it on for the benefit of anyone else landing here with the same issue
Issues Summary and clarification of a couple of points given already
The VBA Input Box Function ( and the VBA MsgBox Box Function) are Modal, in other words you cannot do anything to the spreadsheet when they are up. They seem to work well.
The Application Input Box Method should allow you to do this spreadsheet selection when you choose the extra last Type:= option it has as , Type:=8. ( In fact the spreadsheet selection is available for all Type:= options, but the Type:=8 ( Range ) was of particular interest to the OP as it is to me. The VBA Input Box Function only allows you to type in string information in the Box Input Bar )
The Application Input Box Method does allow you to do this Type:=8 ( Range ) via spreadsheet selection, but a couple of things are broken:
_ The ability to position the Pop up ( appears to be broken since Excel 2007 )
_ The Microsoft help function does not appear to me to work in Excel 2003 2007 2010. I do not know if it ever worked for the Application Input Box Method
A solution I am using now
( This is specifically not a solution with a UserForm as I am also considering that separately for my work as a comparison). This is a Pop Up User pseudo InputBox with range selection alternative using API Programs.
This solution overcomes these problems, which is the main reason I did it, especially because of the first problem, which is the main issue of this Thread. It also has a few extra things that might be useful:
_ You can choose the size of the Pop up ( width , height )
_ You can adjust the “z” things… I am not too clear on these options but in simple terms it means that you arrange how it appears in terms of the order of what windows you see, how and in which priority you see it, what windows are “under” or “above” it to see etc..
_ A simple change of the ByRef to ByVal in the signature line of a Call ed routine allows you to change the value of a range object to that of your selection, but the original range object will not change, that is to say its address remains as before the selection. That could give you an extra option in how you select and move around in a spreadsheet.
All codes should be copied to the same code module. ( If the detailed comments irritate then you can easily tank them all with a couple of clicks – see here: mrexcel.com/forum/about-board/795476-comments-code.html#post3893448 )
The first sections Rem 1 and Rem 2 makes the necessary API programmes available and declares (Dim’s) a few related globial variables. This section will need to go at the top of a code module.
Rem 1 is straight forward and makes available a pseudo Non Modal message box.
Rem 2 is a bit more complicated and makes available a few API program things needed to mess about with Windows when they come up.
Option Explicit ' “Window"s is a name for a programming idea which might result in something we “see” as what we conceive as Windows. Manipulating of the actual “Windows” seems the key to pseudo “making my own” InputBox with range selection. Direct linked libraries (dll) are available to run as and when required, hence the wording of direct link: They are used as an efficient means to organise Microsoft’s software generally allowing different Applications to share smaller programs which are shipped as standard with the Microsoft Windows Operating system. They are however also available to programmers , programming the applications. They are usually contained in Folder with name similar to User 32. "API calls”: just means usually that you are using those things and related “Windows” concept-all gets gets bundled up in imprecise intimidating term API, for Application Programming interface
Rem 1 Pseudo Non Modal MsgBox, MessageBoxA API Standard Non Standard Stuff, More Fundamentally complicated UnWRap it and.. "Pseudo Non Modal MsgBox" --- A valid handle, hWnd, other than the Excel spreadsheet window ( Private Declare Function FindWndNumber Lib "user32" Alias "FindWindowA" (Optional ByVal lpClassName As String, Optional ByVal lpWindowName As String) As Long --- hWndParent = FindWndNumber(lpClassName:="XLMAIN", lpWindowName:=vbNullString) ), or even no ( Null ) hWnd results in a pseudo Non Modal MsgBox http://www.excelfox.com/forum/showthread.php/2227-VBA-Input-Pop-up-Boxes-Application-InputBox-Method-versus-VBA-InputBox-Function?p=10476#post10470 http://www.tek-tips.com/faqs.cfm?fid=4699
Private Declare Function APIssinUserDLL_MsgBox Lib "user32" Alias "MessageBoxA" (Optional ByVal hWnd As Long, Optional ByVal Prompt As String, Optional ByVal Title As String, Optional ByVal Buts As Long) As Long '
'_- ==== The above is all I need to do so that writing APIssinUserDLL_MsgBox in any code in this code module will do something very similar to the VBA MsgBox. The main difference is that when it is up, I can still scroll up and down in my Excel Spreadsheet and also select a range.
Rem 2_b)(ii) == To set/change The positional arguments "Sub Classing a "Window"" As is generally the case with “Window” Functions, A window belongs to a class. The Dynamic Linked Libraries concept allow the small programs in the with windows shipped typically in the User32 Folder programs to be called up / used at runtime, rather than a fixed set of instructions copied or and/ or used as such at some point. This allows for a modification of the class, known as Sub classing. This means that it is possible to modify / add to the “Window” Function and so pseudo create a customised ddl. It does not necessarily mean that a “Window” Function or a used User32 Folder program is directly Sub classes , but it just happens to be in our case as we are intending to mess about with the MessageBoxA ( or MessageBoxTimeoutA ) You can arrange that a used “Window” Function is modified as it is used.
' The next four line will tie something on my chain for when you pull it. Similar in the way that a Worksheet_change code is triggered as something happens, you must arrange that a VBA Function is triggered when a Windows “event” occurs. At this point the concept gets a bit vague and I doubt many people really understand anymore how it really works. A good name for the VBA Function might be Function WinSubWinCls_JerkBackOffHooKterd. This VBA Function will itself be a pseudo “Window” Function and “hung” or hooked on a chain of events. Because of the dynamic / volatile nature of the stuff, things will have a habit of going on forever if they not “unhooked” such that a procedure will have to be designed to unhook itself.
Private Declare Function SetWindowsHooksExample Lib "user32" Alias "SetWindowsHookExA" (ByVal Hooktype As Long, ByVal lokprocedureAddress As Long, Optional ByVal hmod As Long, Optional ByVal DaFredId As Long) As Long ' The effect of this will be: In some predetermined set of instructions or planned chain of events, a “hook” or “marker” or “clap trap” or “page marker” or “trip trap” was made. This was given an identifying number which was returned by that “API Function” and it was chosen to be placed in the main code in a globial variable hHookTrapCrapNumber. I do not think that this number identifies the “page in the book” where the bookmarker is. I think it just is listed somewhere in a list of any active / set up book marks. I guess there might be / could be a few, so you need to distinguish them.
Dim hHookTrapCrapNumber As Long ' "BookmarkClassNumber --- This makes pseudo Declare Sub() SetWindowsHooksExample Lib "user32" AliAs "SetWindowsHookExA" (ByVal hHookTrapCrapNumber As Long, ByVal Hooktype As Long, ByVal MyloksPROCedureFukAddress As Long, Optional ByVal RadioButton2Out As Long, Optional ByVal duhFredId As Long) As Long It was also disgust that possibly the number refers to set instances of a Bookmark class: there may be a few , but they are all effectively connected / activated by the number hHookTrapCrapNumber existing in some register. The bookmarker has a particular type, ( 5 ). The type will be responsible for catching the Message box code line to call the MessageBoxA, (like APIssinUserDLL_MsgBox &H0, "Select Range", "working ApplicationPromptToRangeInputBox", vbYesNoCancel ). That fires my hook PROCedure Function, WinSubWinCls_JerkBackOffHooKterd. Other things may also fire my hook PROCedure Function. They may or may not be also related to the Popping up of my Box.
' Dim BookMarkClassTeachMeWind As Long: Let BookMarkClassTeachMeWind = 5 ' 5 is Hooktype that I will be using. Using a variable for two reasons: '1_- general point in computing that you might get problems when a number is used to refer to something that might take or give a number at some point. But you might need to do that, so having an intermediate word is a workaround for that so that the number is set to a word which is then related to a word that might be being referred to or returning a number. Function = Word --- Word = 873248 '2_- Just to avoid confusion Later as in this particular case later another option number happens to be 5
Private Declare Function GetDaFredId Lib "kernel32" Alias "GetCurrentThreadId" () As Long ' The Thread is what is going on, I expect that means in this case my VBA. My computer might do something else with or without me knowing. Most things going on will have a Thread number. When used in my code, Function GetDaFredId will return an identifying number referring to the Excel instance that that code line is in. It is actually needed in the setting of the Windows hook code line only ( that which is last argument in SetWindowsHookEx( , , , DaFredId As Long) ... set a hook, confined to the current thread (so it doesn't get triggered by other things going on) and give it the address of the function that you want to call in response to the hook being triggered. In this I will use 5 CBT hook which is triggered generally by Window messages (activating, creating, destroying, minimizing, maximizing, moving, or sizing a window)
' This below takes it off the chain. Or wipe the chain clean. Or remove it from something. Or cancel it. Or Kill it. Or whatever. In any case it needs the identifying number of the "hook", then a simple code line as shown in comment below will do this "Killing" Without doing this the thing seems to go on indefinitely (with or without any recursion. (A recursion is another issue which seems to happen as an additional issue - that occurs when the final API code below (over next line) does its job - that seems to fire my Hook PROCedure function WinSubWinCls_JerkBackOffHooKterd
Private Declare Function UnHookWindowsHookCodEx Lib "user32" Alias "UnhookWindowsHookEx" (ByVal hHookTrapCrapNumber As Long) As Long ' 'Release the Hook This is used in code in a simple code line like:- Call UnHookWindowsHookCodEx(hHookTrapCrapNumber)
'_- === All of the above in section Rem 2 is required so that I am able to organise that when I use APIssinUserDLL_MsgBox another program (my windows hookProcedure program WinSubWinCls_JerkBackOffHooKterd) is triggered. (It has a habit of being triggered indefinitely so the API program Decared in the last line above will be used to stop that happening).
'2(d)=== The Final API program below we need to actually do what we want. (WindowIdentifyinghandle, zorder , x , y , width , height , zFurtherInfo ) '_- Most is obvious, except the z stuff - WindowIdentifyinghandle/wParam is one the parameters passed in some secret process to my Function WinSubWinCls_JerkBackOffHooKterd( , wParam , ) and will be the windows identifying number for my Message box that is popping up. ( ,10 ,50 ,400 ,150 , ) These four numbers are the horizontal and vertical size and positions. ( 0, , , , ,40 ) The two numbers 0 and 40 are chosen after a bit of intuitive guessing based on Microsoft references like https://msdn.microsoft.com/en-us/library/windows/desktop/ms633545(v=vs.85).aspx The end effect is to have the window seen as dominantly as wanted. They are likely to be based to some extent on experimenting in a particular requirement.
Private Declare Function SetWindowPosition Lib "user32" Alias "SetWindowPos" (ByVal hWnd As Long, ByVal zNumber As Long, ByVal CoedX As Long, ByVal CoedY As Long, ByVal xPiXel As Long, ByVal yPiYel As Long, ByVal wFlags As Long) As Long ' This API prog will be called in my hook PROCedure function. So.. Rem 2a)-c) sets "Bookmark"/ series of "Bookmarks"/ Microsoft Windows cyber Robot monitering events (of "type 5", i.e. my Pop up coming up is one such. When such a event occurs my function is triggered by Windows software monitering Robot, he knows where/which my function WinSubWinCls_JerkBackOffHooKterd is from the AddressOf in a "hook setting code line" like (5 ,AddressOf WinSubWinCls_JerkBackOffHooKterd , 0, ThreadID) The monitoring Robot program thing passes somehow (a number from a list of event types to tell me more precisely what event it noticed, wParam-identifying number of the Window doing that event, possibly some other mouse thing info thing am not bothered about)
Dim Booloks As Boolean ' I use this in the code line Booloks = SetWindowPosition(WindowIdentifyinghandle, zorder,x, y, width, height ,zFurtherInfo) I don't seem to need this, but as a function, the SetWindowPos is designed to return a value. In this usage I have not experienced problems using it as a Sub routine Call like Call SetWindowPosition( , , , , , , ) but to be on the safe side I have used it as a Function returning its return in a Boolean variable, Booloks
Dim GlobinalCntChopsLog As Long ' I use this to keep track of the copy number of my Hook PROCedure function WinSubWinCls_JerkBackOffHooKterd, that is to say check for when that = 2. If that is the case I do the "unhooking" and Exit the Function
Section 3a) would be your main code in which you wanted to use / call up the Pop Up User pseudo InputBox with range selection
The simple demo I have done helps illustrate that thing I mentioned about the change of the ByRef to ByVal. If you play around with the code and change that (ByXxx) at the start of the next code section, then I think you will get the point of what I am suggesting there
So in section 3a) the main thing related to the issue is the line
Call HangAHookToCatchAPIssinUserDLL MsgBoxThenBringThatMsgBoxUp(RSel)
That code line makes the pop up come up, and the selected spreadsheet range is returned as a range object in the variable RSel. The code will wait until you have made the selection, but it will not prevent you from making the selection. So in that respect it works similarly to the Application Input Box Method ( when you use that Application Input Box Method and choose the last option as Type:=8 ).
' ========================
Rem 3a) This is just to demo the idea of a Pop Up User InputBox with range selection alternative with API User 32 dll Programs. ' Normally in this section 3a) there would be other stuff and probably lots of it and if I have anything to do with it then it will be very Pretty.. Pretty well disgusting probably.
Sub MainSubWithAllOtherStuffInIt() ' This would be your main coding and would nornally be a lot bigger, it is just here as part of the demo for a Pop Up User InputBox with range selection alternative with API User 32 dll Programs
' Some where in the main code I might want to ask the user to select a range. So to do that I
Dim RSel As Range ' This is a variable to hold the Pointer to the users range object.. So this variable in VBA is like the Link to the part of a URL string reducing size site where a few things about the actual Final site is informed about. This area in that site, like a pigeon Hole to which the variable refers, ( the "pigeon hole" location address, and all its contents would be defined as the "Pointer". Amongst other things it has a link, a "Pointing part", pointing to actually where all the stuff is
'Set RSel = Selection ' This line will be needed if you chose to send ByVal. That is necerssary to ensure that you have a range object - If you do not have a range object when you go to HangAHookToCatchAPIssinUserDLL_MsgBoxThenBringThatMsgBoxUp(ByVal RcelsToYou), then you wont have one when you get back neither, as in HangAHookToCatchAPIssinUserDLL_MsgBoxThenBringThatMsgBoxUp you will be Set ing the copy variable, not the actual RSel variable. You put a copy of the Pointer in the new variable. But it is an object. A different object. A Copy object. https://www.excelforum.com/excel-programming-vba-macros/1138804-help-understanding-class-instancing-cant-set-ws-new-worksheet-intellisense-offers-it-2.html#post4386360
Call HangAHookToCatchAPIssinUserDLL_MsgBoxThenBringThatMsgBoxUp(RSel) ' In a normal application of the main Theme of all this, this would be the main code line you use to cause a the "Pop Up User pseudo InputBox with range selection alternative with API User 32 dll Programs"
VBA.MsgBox Prompt:="Address check RSel - It is now " & RSel.Address & "" & vbCrLf & "Da .Value of the range object is " & RSel.Value ' Just done to demo that A simple change of the ByRef to ByVal in the signature line of a Called routine allows you to change the value of a range object to that of the selection, but the original range object will not change, that is to say its address remains as before the selection.
End Sub
Section 3b)-3c) does two main things.
_ A “Hook” is “Hanged” which does both
__ “catch” events similar to my “Non Modal message box” popping up,
and then when it does it
__ triggers off a Function WinSubWinCls JerkBackOffHooKterd
I have tried to explain everything in more detail in the ‘Comments (and first reference below.)
What actually appears to happen in end effect is that typically as my message box Pops up the function is triggered 6 times before it gets to the “event” I actually want which is the Message box window being activated
Private Sub HangAHookToCatchAPIssinUserDLL_MsgBoxThenBringThatMsgBoxUp(ByRef RcelsToYou) ' This will by referral To You, (RSel), the actual Pointer of you the original RSel. This is not too important a point here, but intersting if you consider the next line alternative to this one.....
' Public Sub HangAHookToCatchAPIssinUserDLL_MsgBoxThenBringThatMsgBoxUp(ByVal RcelsToYou) The RSel Pointer aint Gone anywhere if you do this. Just a copy of the Pointer is here. This will allow you to change the value as the Pointer or a copy of it will tell you where to go and do that... But in neither this line or the last line case have you sent the range object. If you use this line then you will find that the address of the range object will not change, as that refers to the range object of the copy variable in this subroutine. But that will not change the range object of RSel
Set RcelsToYou = Selection ' 3c(-i) Pointer GoneTo -1 WTF
Noughty: ' 3c(0i) Pointer GoneTo 0y WTF
' 3b) Hang A Hook to catch things like APIssinUserDLL_MsgBox, .... HOOK: Hook the pseudo Windows Sub Class Function WinSubWinCls_JerkBackOffHooKerd
Dim BookMarkClassTeachMeWind As Long: Let BookMarkClassTeachMeWind = 5 ' I do not need this. 5 is Hooktype that I will be using. Using a variable for two reasons: '1_- general point in computing that you might get problems when a number is used to refer to something that might take or give a number at some point. But you might need to do that, so having an intermediate word is a workaround for that so that the number is set to a word which is then related to a word that might be being referred to or returning a number. Function = Word --- Word = 873248 '2_- Just to avoid confusion later as in this particular case later another option number in Rem 4 happens to be 5. That is checking for a Window opening. So it is similar to the 5 of BookMarkClassTeachMeWind, but it is a narrowed down version of those window happening things. So a bit of aa coincidence really. Using the variable just reminds me of that.
Let hHookTrapCrapNumber = SetWindowsHooksExample(BookMarkClassTeachMeWind, AddressOf WinSubWinCls_JerkBackOffHooKerd, 0, GetDaFredId) ' (5-pull before flush, somehow arranges that the function gets called ,
' 3c) Bring APIssinUserDLL_MsgBox up
Dim Valyou As Variant: Let Valyou = RcelsToYou.Value: If IsArray(Valyou) Then Valyou = Valyou(1, 1) 'For display Value of Top Left of Selection
Dim Rpnce As Long ' Long is very simple to handle, - final memory "size" type is known (123.456 and 000.001 have same "size" computer memory ) , and so a Address suggestion can be given for the next line when the variable is filled in. '( Long is a Big whole Number limit (-2,147,483,648 to 2,147,483,647) If you need some sort of validation the value should only be within the range of a Byte/Integer otherwise there's no point using anything but Long.--upon/after 32-bit, Integers (Short) need converted internally anyways, so a Long is actually faster. ) https://www.mrexcel.com/forum/excel-questions/803662-byte-backward-loop-4.html
Let Rpnce = APIssinUserDLL_MsgBox(hWnd:=&H0, Prompt:="Yes, or No to ReCheck, Cancel for help ", Title:="Selection Check: Address is " & RcelsToYou.Address & " Value is """ & Valyou & """", Buts:=vbYesNoCancel) ' ' Pseudo Non Modal MsgBox
Set RcelsToYou = Selection: Let Valyou = RcelsToYou.Value: If IsArray(Valyou) Then Valyou = Valyou(1, 1) 'The code waited until you made one of the three message box options. But in this time you could change the selection object
If Rpnce = 2 Then Application.Help HelpFile:=ThisWorkbook.Path & "\AnyFileName.chm", HelpContextID:=2 ' ----- download this file: https://app.box.com/s/bx2pkvtemsppscz60rd6f430wm89c6fj This is a “.chm Microsoft Help file” It has the name _ AnyFileName.chm --- Put in same folder as this Workbook --- Check out possible workarounds --- http://www.excelfox.com/forum/showthread.php/2146-%E0%A4%AC%E0%A5%8D%E0%A4%B2%E0%A5%89%E0%A4%97-%E0%A4%95%E0%A5%8B%E0%A4%B6%E0%A4%BF%E0%A4%B6-%E0%A4%95%E0%A4%B0-%E0%A4%B0%E0%A4%B9%E0%A4%BE-%E0%A4%B9%E0%A5%88-%D8%A8%D9%84%D8%A7%DA%AF%D8%B2-%DA%A9%DB%8C-%DA%A9*Trying-Blogs?p=10467#post10467 --- you shopuld get this HelpGetUpBollox.JPG imgur.com/KdKOYWr
If Rpnce = 7 Then GoTo Noughty ' Option to update the displayed Address and Value in Top Left cell of that range
End Sub
Section Rem 4 is the Function WinSubWinCls JerkBackOffHooKterd
This picks out the specific event I want, that being my “Non modal message box” being activated, and changes the window dimensions, using the API thing SetWindowPos
( You will need to experiment about with, mainly, the middle four numbers 10 50 400 150
The two numbers on the ends, 0 40 , you may want to adjust as well. Understanding those two numbers fully is a bit beyond me – it took me best part of a week to figure out WTF all the other stuff was about ).
The last thing this function does is “kill” or “drop” or ““take off” the “Hook”” or ““Unhook” the hook”. If you don’t do that the function seems to be triggered indefinitely.
A last thing on a similar point: Some other codes doing something similar that I saw, seemed to cause a wild recursion: The size adjustment done in the function by SetWindowPos seemed to set off the function code again. The stack seemed to be limited to 30. I could not see any reason to do that, and in fact it seemed to sometimes cause some weird inconsistent ghostly images to appear on my desktop, sometimes permanently!. My function just does the thing that I think it should do once. It appears more stable. I did have a lot of fun braking things before I got the API stuff correct. But since then the code seems to work well without problems in a number of code situations on different computers and different Excel versions.
( In all the situations that I have tried, my code has Exit-ed as I have expected after an If clause detects if the function is on the start of a first recursion copy run. ( I think it would probably do no harm to unhook directly after the SetWindowPos as well***, just in case the SetWindowPos does not cause recursion. ( ***It seems to do no harm to attempt to “unhook” a few times) )
Rem 4============= Some hidden function / bookmark / bookmarks / cyber Robot thing was brought into life ("I hung or set a hook"). That monitors events like my message box popping up. When it catches one it starts this finction and passes to it three parameters. The first tells me with a number more excactly what even took place, the next is the window identifying number of that window doing that particular event , the last parameter is something maybe to do with the mouse god knows what exactly probably even Sid don't know... but looking at his Avatar I probably wouldn't say that to him as he I don't know if I would want to mess with him...
Private Function WinSubWinCls_JerkBackOffHooKerd(ByVal lMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long ' I "set a hook" which should trigger ( things similar to my Meassage box popping up , and gave it the AddressOf this function , 0 , and limited it to this "Thread" on my computer that is to say my Excel )
Let GlobinalCntChopsLog = GlobinalCntChopsLog + 1 ' The idea of this is that I add 1 on entering and subtract 1 when leaving the function. So this would be two if I started an other copy of this code before the first had finished. I am expecting that as the SetWindowPosition seemes to trigger it off again.
If GlobinalCntChopsLog = 2 Then Let GlobinalCntChopsLog = GlobinalCntChopsLog - 1: UnHookWindowsHookCodEx hHookTrapCrapNumber: Exit Function ' If I have 2 then that is an indication that recurtion has taken place, that is to say I started another function run caused by SetWindowPosition triggering it off. So i assume then that SetWindowPosition has done what it should so I can "take the hook off" (as if i did not then the function seems to get triggeredt indefinitely even without recusion), and then I exit the function. So I do expect a second copy of the code to run, but due to this it does not do anything other than take the "hook off". I also reduce the count by 1. It is then at 1. But then the first copy of the function ends from just under SetWindowPosition. So then the count is reduced again and is at the initial 0
If lMsg = 5 Then Let Booloks = SetWindowPosition(wParam, 0, 10, 50, 400, 150, 40) ' 5 here is the number for a window about to be activated. This is probably the one I want. If I catch it when it is starting , 3, then It might then re set the size and position stuff again to the standard after I have done it
Let GlobinalCntChopsLog = GlobinalCntChopsLog - 1 ' Every first copy run of the code has the count reduced to 0 so that when it starts again (as the only first copy active) it will be increased to 1 again to indicate it is a run of the function copy 1
End Function
Hope that might help the OP and others with the same or similar issues.
Alan
References:
http://www.excelfox.com/forum/showthread.php/2227-VBA-Input-Pop-up-Boxes-Application-InputBox-Method-versus-VBA-InputBox-Function
http://www.mrexcel.com/forum/excel-questions/447043-left-top-arguments-application-inputbox-method.html
http://www.vbforums.com/showthread.php?617519-RESOLVED-Excel-InputBox-position-works-in-2003-but-not-2007
https://www.excelforum.com/excel-new-users-basics/1099015-vba-application-inputbox-option-helpfile-helpcontextid.html#post4827566
Excel versions 2007-2013 do not respect the left and top values. When the input box is called you can drag it to your desired location and it will remember that location going forward.