Welcome to MSDN Blogs Sign in | Join | Help

Craig Skibo's WebLog

Sorry I can't answer the phone right now, I am outside feeding yogurt to the clowns.
Using the clipboard

Sorry that I have not made a post in a while, but I have been very busy at work lately fixing bugs in the Whidbey object model, as well as working to get a new way of writing wizards ready (more info on that in a few days).

 

Not too long ago I was browsing the net, and found a code snip that I sent to somebody describing how to use the clipboard from a macro. Because of the way the Macros IDE runs macros on behalf of Visual Studio, there are some problems when trying to access some features of the .NET frameworks. An example of this problem is when trying to access the clipboard. If you were to run this code:

 

    Sub GetCBData()

        Dim o As System.Windows.Forms.IDataObject

        Dim s As String

        o = System.Windows.Forms.Clipboard.GetDataObject()

        s = o.GetData(System.Windows.Forms.DataFormats.StringFormat)

        MsgBox(s)

    End Sub

 

You would get back an “Object reference not set to an instance of an object.” exception. How do you work around it? You will need to create a new thread, and set the clipboard from within that thread. Here is a class to do just such a thing:

 

Public Class MacroClipBoard

    Dim str As String

 

    Function GetFromProxy() As String

        Dim t As New System.Threading.Thread(AddressOf GetFrom)

        t.Start()

        t.Join()

        Return str

    End Function

 

    Sub CopyToProxy(ByVal s As String)

        str = s

        Dim t As New System.Threading.Thread(AddressOf CopyTo)

        t.Start()

        While (t.IsAlive)

            'Do nothing. Wait for the thread to do its work.

        End While

        t.Join()

    End Sub

 

    Sub GetFrom()

        Dim o As System.Windows.Forms.IDataObject

        o = System.Windows.Forms.Clipboard.GetDataObject()

        str = o.GetData(System.Windows.Forms.DataFormats.StringFormat)

    End Sub

 

    Sub CopyTo()

        System.Windows.Forms.Clipboard.SetDataObject(str, True)

    End Sub

 

End Class

 

To use this class, you would run code such as this:

 

    Sub testcb()

        Dim macroCB As New MacroClipBoard

        macroCB.CopyToProxy("Some random string")

        MsgBox(macroCB.GetFromProxy)

    End Sub

 

This code only works with string data types, but you could easily modify it to return other types such as bitmaps.

 

Edit 2/16: I made a small change to the CopyToProxy function, replacing the strike-through text with “t.Join()”

Posted: Saturday, February 14, 2004 12:45 PM by CraigSkibo
Filed under: , ,

Comments

Mike Dimmick said:

OK, dumb question: why can't CopyToProxy simply call t.Join, rather than spinning on t.IsAlive?
# February 15, 2004 4:19 AM

Woo Seok Seo said:

Could you tell me why I need to create a thread?
# February 15, 2004 4:52 PM

Craig said:

Why didn't I use t.Join? Because when I wrote the code it was immediately returning, leaving str unassigned. I put that loop in there to watch the variable to see when it was being set, but never switched my code over. Doh!
# February 16, 2004 10:42 AM

Craig said:

The reason this happens is that the clipboard code in the URT wants the caller to be single threaded. Macros, however, run in a multi-threaded environment. The two threading models conflict and the exception is generated.
# February 16, 2004 10:45 AM

Koji Ishii said:

I can barely understand this is STA problem, but I don't understand why this code works. This code creates more threads, and calls SetDataObject from non-UI thread. I'm curious why making yet another thread, which is apparently non-UI, not having message-pump, thread helps this problem. Any further explanations would be appreciated.
# February 26, 2004 10:30 PM

peterhcy said:

Could you tell me how to install and set up a blog?
# July 20, 2004 6:24 AM

Drax said:

This information is really usefull!!! thanks a lot!!!
# August 3, 2004 5:17 AM

Jeff Atwood said:

Note that in VS.NET you *MUST* set the threadingmodel for the thread to STA explicitly:

Dim t As New Threading.Thread(AddressOf GetClipboardPrivate)
t.ApartmentState = Threading.ApartmentState.STA
t.Start()
t.Join()
# October 27, 2005 4:02 AM

Jeff Atwood said:

Sorry, I meant in VS.NET *2005*. That's required now..
# October 27, 2005 4:02 AM

hmt said:

Thank you! The just the answer I've been looking for!

# June 17, 2008 10:39 AM

hmt said:

Thank you! This is the answer I've been looking for!

# June 17, 2008 10:41 AM

dorasoft said:

I used code

Dim t As New Threading.Thread(AddressOf CPKeyPress)

t.ApartmentState = Threading.ApartmentState.STA

t.Start()

t.Join()

But there was error :

Overload resolution failed because no accessible 'New' can be called with these arguments:

   'Public Sub New(start As System.Threading.ParameterizedThreadStart)': Method 'Private Sub CPKeyPress(sender As Object, HotKey As HotKey)' does not have a signature compatible with delegate 'Delegate Sub ParameterizedThreadStart(obj As Object)'.

   'Public Sub New(start As System.Threading.ThreadStart)': Method 'Private Sub CPKeyPress(sender As Object, HotKey As HotKey)' does not have a signature compatible with delegate 'Delegate Sub ThreadStart()'. D:\Project\Office Assistant\Form\frmMain.vb 80 13 OfficeAssistant

# August 3, 2008 3:55 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

  
Enter Code Here: Required

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Page view tracker