Avatar of jruhe
Flag for United States of America asked on

visual basic 6 macro, the clipboard & third-party software add-on

Hi all,

I'm having an issue with a macro I wrote to get an ID from a third-party product.  I need the ID from the third-party screen (form) to figure out which record I'm on, because there is no data layer in the SDK.  Basically, I created a DLL that adds an option to the product's menu.  When the user clicks the new option on the menu, the program moves the cursor (using Alt-G) to the first field on the form, then "presses" Ctrl-C.  This works great if it is isolated--I can use ctrl-v to paste the contents of the ID field after it runs.  However, as soon as I add anything else to it, like MsgBox Clipboard.GetText, for example, it doesn't work.  Since my whole reason to do the "macro" exercise is to capture the info in the clipboard, I really need to fix this.  Code follows, but please read my last comment, below.

The code is here in a sub of a Functions.bas module:
    ' Alt-G:
    Call keybd_event(vbKeyMenu, 0, 0, 0)
    Call keybd_event(vbKeyG, 0, 0, 0)
    Call keybd_event(vbKeyG, 0, KEYEVENTF_KEYUP, 0)
    Call keybd_event(vbKeyMenu, 0, KEYEVENTF_KEYUP, 0)
    Call keybd_event(vbKeyControl, 0, 0, 0)
    ' Ctrl-C:
    Call keybd_event(vbKeyC, 0, 0, 0)
    Call keybd_event(vbKeyC, 0, KEYEVENTF_KEYUP, 0)
    Call keybd_event(vbKeyControl, 0, KEYEVENTF_KEYUP, 0)

And in the Globals:
Public Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

And in the declarations of the Functions module:
Declare Function apiFindWindow Lib "user32" Alias "FindWindowA" _
   (ByVal lpclassname As Any, ByVal lpCaption As Any) As Long

Final Comment:
I noticed that when it works (if it runs by itself without any code after it), my cursor is where it should be -- in the ID field -- and the whole field is selected.  When I run it with my "msgbox" command (or any command) after it, the focus is not on the ID field.    I'm not sure if this matters but I figured I'd include it in case it helps.

Visual Basic ClassicProgramming

Avatar of undefined
Last Comment

8/22/2022 - Mon

You could assign the value in the clipboard object to a variable, rather than including it in the msgbox statement directly.

If the variable assignment removes the item from the clipboard, you can add it back to the clipboard.

Question: Have you considered SendMessage() API or SendKeys, rather than keybd_event() API?

Thanks for your response a1kimark :)

Yes--I actually only used MsgBox to show the problem--indeed I do need it in a variable and have attempted to do something like MyVar = Clipboard.GetText, but it doesn't work--it's like, if there's any code at all after the keyboard events, the keyboard events don't work.  It may be some kind of quirk with the products' API, but it's undocumented.

Do you have more info on SendMessage() or SendKeys?  I tried SendKeys but it didn't appear to work at all (and ugh--I didn't save the code I used!).  That said, I've never used anything like this, so if you've got a good example of either, I'd like to see it.  


View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.

I considered this (or something akin -- note I don't have access to the interface itself so I can't populate a textbox and it looks like I can't use SendKeys if that's what it does).   It was just supposed to populate the clipboard contents on a form that I don't have access to, and then run some code based on that.  However, your suggestion gave me an idea:  perhaps I can create a form whose "visible" property is set to False, and add the timer to it.  The menu command (which I do have access to) could fire the form, then fire the Keyboard Event commands to collect the data then do the rest of the program based on the contents.

I will try that this week. . .  thank you.  meantime, if you know of a timer object that you don't need an interface for, I'd be obliged.

One thing I should "say out loud" before I close. . .  It feels like the clipboard contents are falling out of scope if other commands are added.  It's fine encapsulated (without any commands after the Clipboard GetText command--kind of like it's dabbling within the main program's form and the clipboard (to the user) is still in scope--Ctrl-V will yield the data it copied).  However, once other commands are added, it's almost like the scope of the whole program event stays within my program (as evidenced by the fact that, in the former scenario (no commands after GetText), the cursor ends up in the field that it copied (and all data is selected in that field).  In the latter scenario (commands after GetText--even those that don't mess with the interface--like if a very random MyVar = "abc" was the only command after it copies the clipboard), the cursor ends up off the screen entirely--no field has focus.

I'll try your suggestion this week and truly appreciate your help so far and any thoughts you may have on the above.

I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck

why can't you use Sendkeys?

There is a Sleep() API that you can use in place of a timer.  Be sure to include a DoEvents statement in the loop so that your code can respond to user-directed stop (looping) action.

I was thinking I couldn't use SendKeys because I thought it was an object method--I thought you had to specify the form to use it, but I was incorrect.

I just tested it and ran into the issue that the forms run from within the third-party program/API have to be modal.  When I try to run the Timer form modelessly, the API throws an error.  So I can't use AppActivate().  I'm batting a thousand here, aren't I?  :-)

Checking the sleep() api now. . .  will update when I've got enough info to make it interesting!


Woot Woot!  It worked with a combination of your Timer Control and having the API menu call use an EXE (which will end up containing the rest of the logic). . .  

THANK YOU--you're great!

Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.

Timely, concise--what I needed.  Thank you