export Excel graphs as *.emf - excel

I found that post investigating, but unfortunately not answering the question which came to my mind on
HOW TO EXPORT GRAPHS FROM EXCEL AS *.EMF
Excel export chart to wmf or emf?
The code presented is not working for me.
What I did is to extend each "Private Declare Function" like this "Private Declare PtrSafe Function" to make it applicable for 64BIT.
CODE:
Option Explicit
Private Declare PtrSafe Function OpenClipboard _
Lib "user32" ( _
ByVal hwnd As Long) _
As Long
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
Private Declare PtrSafe Function GetClipboardData _
Lib "user32" ( _
ByVal wFormat As Long) _
As Long
Private Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long
'// CreateMetaFileA DeleteEnhMetaFile
Private Declare PtrSafe Function CopyEnhMetaFileA _
Lib "gdi32" ( _
ByVal hENHSrc As Long, _
ByVal lpszFile As String) _
As Long
Private Declare PtrSafe Function DeleteEnhMetaFile _
Lib "gdi32" ( _
ByVal hemf As Long) _
As Long
Public Function fnSaveAsEMF(strFileName As String) As Boolean
Const CF_ENHMETAFILE As Long = 14
Dim ReturnValue As Long
OpenClipboard 0
ReturnValue = CopyEnhMetaFileA(GetClipboardData(CF_ENHMETAFILE), strFileName)
EmptyClipboard
CloseClipboard
'// Release resources to it eg You can now delete it if required
'// or write over it. This is a MUST
DeleteEnhMetaFile ReturnValue
fnSaveAsEMF = (ReturnValue <> 0)
End Function
Sub SaveIt()
Charts.Add
ActiveChart.ChartArea.Select
Selection.Copy
If fnSaveAsEMF("C:\Excel001.emf") Then
MsgBox "Saved", vbInformation
Else
MsgBox "NOT Saved!", vbCritical
End If
I want to use this code to export graphs from worksheets with their worksheet name automatically to a specific folder within a loop in case thats possible. Highlight would be if its possible to execute that via a button.
So far when i run the code All I get is a "NOT SAVED" message. Im using Excel 365 ProPlus, in case thats of any relevance.
I would highly appreciate if someone would explain me how this code is working and what i need to implement there

The OP code worked for me after commenting out the Charts.add line and changing the write destination to a path where I had write privileges
Option Explicit
Private Declare PtrSafe Function OpenClipboard _
Lib "user32" ( _
ByVal hwnd As Long) _
As Long
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
Private Declare PtrSafe Function GetClipboardData _
Lib "user32" ( _
ByVal wFormat As Long) _
As Long
Private Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long
'// CreateMetaFileA DeleteEnhMetaFile
Private Declare PtrSafe Function CopyEnhMetaFileA _
Lib "gdi32" ( _
ByVal hENHSrc As Long, _
ByVal lpszFile As String) _
As Long
Private Declare PtrSafe Function DeleteEnhMetaFile _
Lib "gdi32" ( _
ByVal hemf As Long) _
As Long
Public Function fnSaveAsEMF(strFileName As String) As Boolean
Const CF_ENHMETAFILE As Long = 14
Dim ReturnValue As Long
OpenClipboard 0
ReturnValue = CopyEnhMetaFileA(GetClipboardData(CF_ENHMETAFILE), strFileName)
EmptyClipboard
CloseClipboard
'// Release resources to it eg You can now delete it if required
'// or write over it. This is a MUST
DeleteEnhMetaFile ReturnValue
fnSaveAsEMF = (ReturnValue <> 0)
End Function
Sub SaveIt()
'Charts.Add
ActiveChart.ChartArea.Select
Selection.Copy
If fnSaveAsEMF("m:\mpo\autompo\test.emf") Then 'the name excluding the .emf can be changed
'Be sure you have write privileges here or you will get an error
MsgBox "Saved", vbInformation
Else
MsgBox "NOT Saved!", vbCritical
End If
End Sub
This is effectively the same answer as #kuv , but adds in the PtrSafe modifier to the windows function calls (these are required with 64 bit excel.

this is some code that i have used, the user32 function which imitates human interaction directly is the only way ive come across to save chats as different formats via vba, also its current statements are for an active sheet/workbook which can obviously be altered if you building a dashboard where the charts remain on other sheets, if you have any queries you can email me on howtovba#gmail.com;
Option Explicit
Private Declare Function OpenClipboard _
Lib "user32" ( _
ByVal hwnd As Long) _
As Long
Private Declare Function CloseClipboard Lib "user32" () As Long
Private Declare Function GetClipboardData _
Lib "user32" ( _
ByVal wFormat As Long) _
As Long
Private Declare Function EmptyClipboard Lib "user32" () As Long
'// CreateMetaFileA DeleteEnhMetaFile
Private Declare Function CopyEnhMetaFileA _
Lib "gdi32" ( _
ByVal hENHSrc As Long, _
ByVal lpszFile As String) _
As Long
Private Declare Function DeleteEnhMetaFile _
Lib "gdi32" ( _
ByVal hemf As Long) _
As Long
Public Function fnSaveAsEMF(strFileName As String) As Boolean
Const CF_ENHMETAFILE As Long = 14
Dim ReturnValue As Long
OpenClipboard 0
ReturnValue = CopyEnhMetaFileA(GetClipboardData(CF_ENHMETAFILE), strFileName)
EmptyClipboard
CloseClipboard
'// Release resources to it eg You can now delete it if required
'// or write over it. This is a MUST
DeleteEnhMetaFile ReturnValue
fnSaveAsEMF = (ReturnValue <> 0)
End Function
Sub SaveIt()
Charts.Add
ActiveChart.ChartArea.Select
Selection.Copy
If fnSaveAsEMF("C:\Excel001.emf") Then 'the name excluding the .emf can be changed
MsgBox "Saved", vbInformation
Else
MsgBox "NOT Saved!", vbCritical
End If
End Sub

Related

How can I read a timer value on a network connection window?

I have a process that requires an active VPN connection, but the connection is automatically cut every 8 hours. I need to be able to control that the connection is active and the time left up to the 8 hour limit.
In the properties of the windows connections the time appears (attached capture with the data that I need), but I need to know how to read this data.
Try the next approach, please:
Edited, because of the last request:
Please add two new declarations
Copy the next API functions on top of a standard module:
Option Explicit
Private Declare PtrSafe Function FindWindow Lib "User32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function FindWindowEx Lib "User32" Alias "FindWindowExA" (ByVal hWnd1 As LongPtr, _
ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr
Private Declare PtrSafe Function SendMessage Lib "User32" Alias "SendMessageA" (ByVal hwnd As LongPtr, _
ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As Long
Private Declare PtrSafe Function GetWindowTextLength Lib "User32" Alias "GetWindowTextLengthA" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function GetWindowText Lib "User32" Alias "GetWindowTextA" (ByVal hwnd As LongPtr, _
ByVal lpString As String, ByVal cch As Long) As Long
Private Declare PtrSafe Function GetWindow Lib "User32" (ByVal hwnd As LongPtr, ByVal wCmd As Long) As Long
And the next Constant:
Private Const GW_HWNDNEXT = 2
'Added after editing:__________________
Private Const WM_LBUTTON_DOWN = &H201
Private Const BM_CLICK = &HF5
'______________________________________
In the same standard module, copy the next Sub. Please, take care to change Duration: from the code, with the Spanish correct variant ('DuraciĆ³n' [with the necessary accent]):
Sub DurationAPI()
Dim hwndEth As LongPtr, hwndGen As LongPtr, hwndDurlbl As LongPtr, hwndDur As LongPtr
Dim sStr As String, strWindowTitle As String, durationLbl As String, durT As Date, limitD As Date
'added after editing:_____________________________
OpenWiFiConnectionWindow 'open connection window
AppActivate Application.ActiveWindow.Caption
'_________________________________________________
limitD = CDate("08:00:00")
strWindowTitle = "Estado de Wi-Fi"
durationLbl = "Duration:" 'Please change here with your exact label title (in Spanish...)
'I cannot write duracion: with the necessary accent...
hwndEth = FindWindow(vbNullString, strWindowTitle): Debug.Print Hex(hwndEth)
hwndGen = FindWindowEx(hwndEth, 0&, vbNullString, "General"): Debug.Print Hex(hwndGen)
hwndDurlbl = FindWindowEx(hwndGen, 0&, vbNullString, durationLbl): Debug.Print Hex(hwndDurlbl)
hwndDur = GetWindow(hwndDurlbl, GW_HWNDNEXT): Debug.Print Hex(hwndDur)
sStr = String(GetWindowTextLength(hwndDur) + 1, Chr$(0))
GetWindowText hwndDur, sStr, Len(sStr)
durT = CDate(sStr)
MsgBox Format(limitD - durT, "hh:mm:ss") & " left until connection will be interrupted!", _
vbInformation, "Time to connection interruption"
'Added after editing: ____________________________________________________
Dim hwndClose As LongPtr
'closing the connection window:
hwndClose = FindWindowEx(hwndEth, 0&, vbNullString, "&Close"): Debug.Print Hex(hwndClose)
SendMessage hwndClose, WM_LBUTTON_DOWN, 0&, 0&
SendMessage hwndClose, BM_CLICK, 0, ByVal 0&
'_________________________________________________________________________
End Sub
bis Copy the Sub able to show the necessary connection window:
Private Sub OpenWiFiConnectionWindow()
Dim objApp As Object: Set objApp = CreateObject("Shell.Application")
Dim objFolder As Object: Set objFolder = objApp.Namespace(&H31&).self.GetFolder
Dim interface As Variant, interfaceTarget As Object, InterfaceName As String
InterfaceName = "Wi-Fi" 'Please, check here what is show your "Network Connections" folder. It maybe can be slightly different...
'I tested the code on my Ethernet connection, which not was simple "Ethernet". It was "Ethernet 2"...
For Each interface In objFolder.Items
If LCase(interface.Name) = LCase(InterfaceName) Then
Set interfaceTarget = interface: Exit For
End If
Next
Dim Verb As Variant
For Each Verb In interfaceTarget.Verbs
If Verb.Name = "Stat&us" Then
Verb.DoIt
Application.Wait Now + TimeValue("0:00:01")
Exit For
End If
Next
End Sub
Please, try this Sub first, in order to be sure that it shows the necessary connection window. If it doesn't, please look in the "Network Connections" folder and change InterfaceName with an appropriate one.
Run the above DurationAPI() Sub.
All the necessary windows handlers are returned in Immediate window. If one of them is 0 (zero), there must be checked to understand what is happening... I used Spy++ to find the windows titles/classes...
For a window with English titles, it returns correctly and almost instant the necessary connection duration time.

HeapFree is crashing

Summary:
I have VBA code that collects lots of info and writes it out into one or more worksheets. To improve perf writing lots of info into sheets, I created a class that acts kind of like a buffered copy/paste stream: the caller sends it CSV format strings which it buffers in memory until the buffer is full; when full, it pastes into a sheet, clears the buffer and continues.
Initially, I used Global memory, but then saw on MSDN a recommendation to use Heap rather than Global or Local due to less overhead. So now I'm using Heap.
I'm in the process of adapting everything for 64-bit Office. After doing all the PtrSafe stuff, I can run the code. But now Excel crashes when HeapFree() is called.
Question: Why is it crashing, and what do I need to change to avoid it?
Details:
I've come up with the following that is a minimized sample that repro's the crash: there's a module with a sub I can run for the repro, and the class. This doesn't do the buffering; every call to .SendText will put the text on the clipboard and paste into the active cell.
First the module. This has the following declare statements
' memory APIs
Public Const HEAP_ZERO_MEMORY = &H8
Declare PtrSafe Function GetProcessHeap Lib "kernel32" () As LongPtr 'returns HANDLE
Declare PtrSafe Function HeapAlloc Lib "kernel32" (ByVal hHeap As LongPtr, ByVal dwFlags As Long, ByVal dwBytes As LongPtr) As LongPtr 'returns HANDLE
Declare PtrSafe Function HeapFree Lib "kernel32" (ByVal hHeap As LongPtr, ByVal dwFlags As Long, lpMem As Any) As Long 'returns BOOL
Declare PtrSafe Function lstrcpy Lib "kernel32" Alias "lstrcpyW" (ByVal lpDestString As Any, ByVal lpSrcString As Any) As LongPtr 'returns HANDLE
' clipboard APIs
Public Const CF_UNICODETEXT = 13
Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long 'returns BOOL
Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long 'returns BOOL
Declare PtrSafe Function CloseClipboard Lib "user32" () As Long 'returns BOOL
Declare PtrSafe Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hMem As LongPtr) As LongPtr 'returns HANDLE
Then the sub:
Private Sub TestMemoryBugRepro()
Dim s As String
Dim clip As clsHeapBugRepro
s = Chr(34) & "a" & vbLf & "b" & Chr(34) & ",c" & vbCrLf & "d" & vbTab & ",e" & vbCrLf
Set clip = New clsHeapBugRepro
clip.Initialize &H100
clip.SendText s
'Crash happens during the following
Set clip = Nothing
End Sub
Now the class. The crash occurs during Class_Terminate() when HeapFree is called.
Option Explicit
Private m_hHeap As LongPtr 'handle to the process heap
Private m_hMem As LongPtr 'handle to memory
Private m_pMem As LongPtr 'pointer to locked memory
Private m_cbMem As Long 'size of the memory buffer
Private m_BytesWritten As Long '
'**************************************
' Event procedures
Private Sub Class_Initialize()
m_hHeap = GetProcessHeap()
End Sub
Private Sub Class_Terminate()
If m_hMem <> 0 And m_hHeap <> 0 Then
HeapFree m_hHeap, 0, m_hMem 'CRASH OCCURS HERE
End If
End Sub
'**************************************
' Public methods
Public Function Initialize(Optional bufferSize As Long = &H8000) As Boolean
Initialize = False
m_BytesWritten = 0
If m_hHeap <> 0 Then
m_cbMem = bufferSize
m_hMem = HeapAlloc(m_hHeap, (HEAP_ZERO_MEMORY), m_cbMem)
End If
If m_hMem <> 0 Then Initialize = True
End Function
Public Function SendText(text As String) As Boolean
Dim nStrLen As Long
nStrLen = LenB(text) + 2&
Debug.Assert nStrLen < (m_cbMem + m_BytesWritten)
m_pMem = m_hMem 'in lieu of locking heap memory
lstrcpy m_pMem, StrPtr(text)
m_pMem = 0 'in lieu of unlocking heap memory
m_BytesWritten = m_BytesWritten + nStrLen
DoEvents
OpenClipboard 0&
EmptyClipboard
SetClipboardData CF_UNICODETEXT, m_hMem
CloseClipboard
ActiveCell.PasteSpecial
DoEvents
SendText = True
End Function
There's an issue in how you're calling HeapFree, but your real issue is that you shouldn't be using HeapAlloc/HeapFree at all.
Memory allocated by HeapAlloc is not movable, whereas SetClipboardData requires it to be moveable.
Another consideration is that SetClipboardData transfers the ownership of the memory to the system, which means you should not free it yourself. (The application may not write to or free the data once ownership has been transferred to the system.)
So, I would try to rewrite your logic using GlobalAlloc, not HeapAlloc, and don't try to free memory after it is put on the clipboard.
Now, there was an issue in how you were calling HeapFree, if you were going to use it. HeapFree wants the pointer returned by HeapAlloc.
You were instead passing a pointer to that pointer, because you declared the third argument of HeapFree As Any, which means ByRef As Any.
Either redeclare the argument ByVal As LongPtr / ByVal As Any, e.g.:
Declare PtrSafe Function HeapFree Lib "kernel32" (ByVal hHeap As LongPtr, _
ByVal dwFlags As Long,
ByVal lpMem As LongPtr) As Long 'returns BOOL
or specify ByVal when calling it:
HeapFree m_hHeap, 0, ByVal m_hMem

Focus to MgsBox when Excel is not active

I have checked related questions such as this or this one but the solutions there do not seem to solve my problem.
I am running a VBA script on my computer. The script takes a few minutes to execute and while waiting I am checking other things in my computer. To get my attention once the script has finished running, I have included a MsgBox at the end of my script. However, because Excel is not active/selected when the script finishes, I cannot see it - only when I reactivate/select Excel.
How can I bring into focus the MsgBox when Excel is not active? I have already tried the following tweaks but they do not work:
ThisWorkbook.Activate:
...
ThisWorkbook.Activate
MsgBox "..."
...
AppActivate() (this command threw an error):
...
AppActivate("Microsoft excel")
MsgBox "..."
...
How about playing a sound when the program finishes?
Place this declaration at the top of a standard code module, above any procedures existing there.
Public Declare Function Beep Lib "kernel32" _
(ByVal dwFreq As Long, _
ByVal dwDuration As Long) As Long
If you place this procedure in the same module you may not need it to be public. Adjust pitch and duration to your preference.
Sub EndSound()
Beep 500, 1000
End Sub
Then place the procedure call at the end of your program.
Call EndSound
I suppose you might use a more elaborate sound - may I suggest a couple of bars from Beethoven's 5th? Modify the EndSound procedure. Chip Pearson has more on this idea.
Try:
Application.WindowState = xlMaximized
Disclaimer: This is not my code and I do not know who the author is. I had this code in my database.
Put your code in Sub Sample(). I have shown where you can insert your code. Once the code is run, Excel will flash 5 times. you can change this number by changing Private Const NumberOfFlashes = 5
Paste this in a Module.
Option Explicit
Private Type FLASHWINFO
cbSize As Long
Hwnd As Long
dwFlags As Long
uCount As Long
dwTimeout As Long
End Type
Private Const FLASHW_STOP As Long = 0
Private Const FLASHW_CAPTION As Long = &H1
Private Const FLASHW_TRAY As Long = &H2
Private Const FLASHW_ALL As Long = (FLASHW_CAPTION Or FLASHW_TRAY)
Private Const FLASHW_TIMER As Long = &H4
Private Const FLASHW_TIMERNOFG As Long = &HC
Private FLASHW_FLAGS As Long
Private Declare Function LoadLibrary Lib "kernel32" _
Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" _
(ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function FreeLibrary Lib "kernel32" _
(ByVal hLibModule As Long) As Long
Private Declare Function FlashWindowEx Lib "user32" _
(FWInfo As FLASHWINFO) As Boolean
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Const NumberOfFlashes = 5
Private Function APIFunctionPresent(ByVal FunctionName _
As String, ByVal DllName As String) As Boolean
Dim lHandle As Long
Dim lAddr As Long
lHandle = LoadLibrary(DllName)
If lHandle <> 0 Then
lAddr = GetProcAddress(lHandle, FunctionName)
FreeLibrary lHandle
End If
APIFunctionPresent = (lAddr <> 0)
End Function
Sub Sample()
'
' Put your code here. Once that code finishes, Excel will FLASH
'
Dim udtFWInfo As FLASHWINFO
If Not APIFunctionPresent("FlashWindowEx", "user32") Then Exit Sub
With udtFWInfo
.cbSize = Len(udtFWInfo)
.Hwnd = Application.Hwnd
.dwFlags = FLASHW_FLAGS Or FLASHW_TRAY
.uCount = NumberOfFlashes
.dwTimeout = 0
End With
Call FlashWindowEx(udtFWInfo)
MsgBox "Done"
End Sub
The easiest way is to probably to create a userform instead then set the focus to this when it initialises.
Code in the userform to show as modal:
Private Declare Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName _
As String, ByVal lpWindowName As String) As Long
Private Declare Function SetWindowPos Lib "User32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, _
ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOMOVE = &H2
Private Const FLAGS As Long = SWP_NOMOVE Or SWP_NOSIZE
Private Const HWND_TOPMOST = -1
Private Sub UserForm_Initialize()
Dim hwnd As Long: hwnd = FindWindow(vbNullString, Me.Caption)
If hwnd > 0 Then SetWindowPos hwnd, HWND_TOPMOST, 0, 0, 0, 0, FLAGS ' Set Modal
End Sub

Minimize UserForm when macro in it is running

I am trying to minimize a Userform when it is running a method in it. Functionality in my method is so huge that it is running for long time. I would like to minimize UserForm so that I could work on some other excel sheets and later come back to UserForm (or restore it) during this run.
When UserForm is running, it is not allowing to access any of its components(so that even if i add minimize button its of no use). I am able to access other excels and work on them because i have made Userform as vbmodeless, but my requirement is userform should be minimized, now I am dragging UserForm to the end of the screen to view other files easily.
Why not add a minimize / maximize to your userform ;)
Here is something from my database (Not my Code). Paste this in the userform
Option Explicit
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function DrawMenuBar Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function ShowWindow Lib "user32" _
(ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Const WS_MINIMIZEBOX As Long = &H20000
Private Const WS_MAXIMIZEBOX As Long = &H10000
Private Const GWL_STYLE As Long = (-16)
Private Const WS_SYSMENU As Long = &H80000
Private Const SW_SHOWMAXIMIZED = 3
Private Sub UserForm_Activate()
Dim Ret As Long, styl As Long
Ret = FindWindow("ThunderDFrame", Me.Caption)
styl = GetWindowLong(Ret, GWL_STYLE)
styl = styl Or WS_SYSMENU
styl = styl Or WS_MINIMIZEBOX
styl = styl Or WS_MAXIMIZEBOX
SetWindowLong Ret, GWL_STYLE, (styl)
DrawMenuBar Ret
End Sub
Screenshot

VBA Macro to copy different cell values in a Notepad

I want to copy the different cells data into a notepad. How do i do that.
Example : values from row3 column B(B3), then from row3 column E(E3).
Thanks in advance.
Using a helper module you could:
Dim cell As Range
Dim concat As String
For Each cell In Range("$B$3,$E$3")
concat = concat & vbCrLf & cell.Value
Next
' Debug.Print concat
Text2Clipboard concat
If instead of Range("$B$3,$E$3") you said Selection you'd end up with all the cells currently selected.
Hint: You can select inidivual cells using Ctrl+LeftMouseButton
You need the following helper definitions somewhere in your VBA project (I suggest a module named like 'Clipboard'):
Declare Function abOpenClipboard Lib "User32" Alias "OpenClipboard" (ByVal Hwnd As Long) As Long
Declare Function abCloseClipboard Lib "User32" Alias "CloseClipboard" () As Long
Declare Function abEmptyClipboard Lib "User32" Alias "EmptyClipboard" () As Long
Declare Function abIsClipboardFormatAvailable Lib "User32" Alias "IsClipboardFormatAvailable" (ByVal wFormat As Long) As Long
Declare Function abSetClipboardData Lib "User32" Alias "SetClipboardData" (ByVal wFormat As Long, ByVal hMem As Long) As Long
Declare Function abGetClipboardData Lib "User32" Alias "GetClipboardData" (ByVal wFormat As Long) As Long
Declare Function abGlobalAlloc Lib "Kernel32" Alias "GlobalAlloc" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Declare Function abGlobalLock Lib "Kernel32" Alias "GlobalLock" (ByVal hMem As Long) As Long
Declare Function abGlobalUnlock Lib "Kernel32" Alias "GlobalUnlock" (ByVal hMem As Long) As Boolean
Declare Function abLstrcpy Lib "Kernel32" Alias "lstrcpyA" (ByVal lpString1 As Any, ByVal lpString2 As Any) As Long
Declare Function abGlobalFree Lib "Kernel32" Alias "GlobalFree" (ByVal hMem As Long) As Long
Declare Function abGlobalSize Lib "Kernel32" Alias "GlobalSize" (ByVal hMem As Long) As Long
Const GHND = &H42
Const CF_TEXT = 1
Const APINULL = 0
Function Text2Clipboard(szText As String)
Dim wLen As Integer
Dim hMemory As Long
Dim lpMemory As Long
Dim retval As Variant
Dim wFreeMemory As Boolean
' Get the length, including one extra for a CHR$(0) at the end.
wLen = Len(szText) + 1
szText = szText & Chr$(0)
hMemory = abGlobalAlloc(GHND, wLen + 1)
If hMemory = APINULL Then
MsgBox "Unable to allocate memory."
Exit Function
End If
wFreeMemory = True
lpMemory = abGlobalLock(hMemory)
If lpMemory = APINULL Then
MsgBox "Unable to lock memory."
GoTo T2CB_Free
End If
' Copy our string into the locked memory.
retval = abLstrcpy(lpMemory, szText)
' Don't send clipboard locked memory.
retval = abGlobalUnlock(hMemory)
If abOpenClipboard(0&) = APINULL Then
MsgBox "Unable to open Clipboard. Perhaps some other application is using it."
GoTo T2CB_Free
End If
If abEmptyClipboard() = APINULL Then
MsgBox "Unable to empty the clipboard."
GoTo T2CB_Close
End If
If abSetClipboardData(CF_TEXT, hMemory) = APINULL Then
MsgBox "Unable to set the clipboard data."
GoTo T2CB_Close
End If
wFreeMemory = False
T2CB_Close:
If abCloseClipboard() = APINULL Then
MsgBox "Unable to close the Clipboard."
End If
If wFreeMemory Then GoTo T2CB_Free
Exit Function
T2CB_Free:
If abGlobalFree(hMemory) <> APINULL Then
MsgBox "Unable to free global memory."
End If
End Function
Notes
code 'borrowed' from http://www.everythingaccess.com/tutorials.asp?ID=Copying-data-to-and-from-the-Clipboard-(Acc-95%2B)
tested in Excel 2003 on Windows XP SP3
Write your cell values to a text file, then open it in Notepad or your favourite text editor.
' Write to file.
Open "C:\temp.txt" For Output As #1
Print #1, Range("B3").Value, Range("C4").Value
Print #1, Range("Q981").Value, "hello world!" ' or whatever else
Close #1
' Now open it in notepad.
Shell ("notepad ""C:\temp.txt""")

Resources