I am experimenting with drag and drop from an outlook email attachment to a custom application. It works just fine with C#, but now I'd like to get it working in VB6. My test code looks something like this:
Private Sub grdLis_OLEDragDrop(Data As MSFlexGridLib.DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single)
Dim FileDropped As String * 256
Dim lCF_FILEGROUP As Long
Dim CF_FILEGROUP As Integer
Dim lCF_FILECONTENTS As Long
Dim CF_FILECONTENTS As Integer
Dim bData() As Byte
Dim bFileNameData(256) As Byte
Dim bData2() As Byte
lCF_FILEGROUP = RegisterClipboardFormat("FileGroupDescriptor")
MoveMemory CF_FILEGROUP, lCF_FILEGROUP, 2
bData() = Data.GetData(CF_FILEGROUP)
j = 0
For i = 76 To 76 + 256
bFileNameData(j) = bData(i)
j = j + 1
Next i
FileDropped = Trim(StrConv(bFileNameData, vbUnicode))
lCF_FILECONTENTS = RegisterClipboardFormat("FileContents")
MoveMemory CF_FILECONTENTS, lCF_FILECONTENTS, 2
bData2() = Data.GetData(CF_FILECONTENTS)
This works to get the file name, but it throws an exception on the Data.GetData(CF_FILECONTENTS) call, and the error messages says "Automation error invalid tymed".
I don't have much experience in VB6, and I have no idea what that error message means or what to do about it. Any help or insight would be appreciated.
Check out Get dropped attachments from Outlook messages for more ideas. It's using OLE heavily.
Related
I have both AutoCAD and AutoCAD Architecture installed on my system. AutoCAD Architecture has a tab called Vision Tools with a nifty command called Display By Layer to set the display order of objects in accordance with the layers of the drawing. Is there anyway to add this tab or use this command in AutoCAD?
Not sure if you're looking for a built-in feature or APIs for it.
For a built in feature, check the DRAWORDER command. For an API/programming approach, check the respective DrawOrderTable method. See below:
Update: please also check this 3rd party tool: DoByLayer.
[CommandMethod("SendToBottom")]
public void commandDrawOrderChange()
{
Document activeDoc
= Application.DocumentManager.MdiActiveDocument;
Database db = activeDoc.Database;
Editor ed = activeDoc.Editor;
PromptEntityOptions peo
= new PromptEntityOptions("Select an entity : ");
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
{
return;
}
ObjectId oid = per.ObjectId;
SortedList<long, ObjectId> drawOrder
= new SortedList<long, ObjectId>();
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = tr.GetObject(
db.BlockTableId,
OpenMode.ForRead
) as BlockTable;
BlockTableRecord btrModelSpace =
tr.GetObject(
bt[BlockTableRecord.ModelSpace],
OpenMode.ForRead
) as BlockTableRecord;
DrawOrderTable dot =
tr.GetObject(
btrModelSpace.DrawOrderTableId,
OpenMode.ForWrite
) as DrawOrderTable;
ObjectIdCollection objToMove = new ObjectIdCollection();
objToMove.Add(oid);
dot.MoveToBottom(objToMove);
tr.Commit();
}
ed.WriteMessage("Done");
}
With some help from VBA it might look by this. Note i did not add fancy listbox code. I just show the worker and how to list layers. The trivial Code to add things to a listbox on a form and how to sort / rearrange listbox items can be found on any excel / VBA forum on the web . Or you just uses a predefined string like in the example. To get VBA to work download and install the acc. VBA Enabler from autocad. It is free.
'select all items on a layer by a filter
Sub selectALayer(sset As AcadSelectionSet, layername As String)
Dim filterType As Variant
Dim filterData As Variant
Dim p1(0 To 2) As Double
Dim p2(0 To 2) As Double
Dim grpCode(0) As Integer
grpCode(0) = 8
filterType = grpCode
Dim grpValue(0) As Variant
grpValue(0) = layername
filterData = grpValue
sset.Select acSelectionSetAll, p1, p2, filterType, filterData
Debug.Print "layer", layername, "Entities: " & str(sset.COUNT)
End Sub
'bring items on top
Sub OrderToTop(layername As String)
' This example creates a SortentsTable object and
' changes the draw order of selected object(s) to top.
Dim oSset As AcadSelectionSet
Dim oEnt
Dim i As Integer
Dim setName As String
setName = "$Order$"
'Make sure selection set does not exist
For i = 0 To ThisDrawing.SelectionSets.COUNT - 1
If ThisDrawing.SelectionSets.ITEM(i).NAME = setName Then
ThisDrawing.SelectionSets.ITEM(i).DELETE
Exit For
End If
Next i
setName = "tmp_" & time()
Set oSset = ThisDrawing.SelectionSets.Add(setName)
Call selectALayer(oSset, layername)
If oSset.COUNT > 0 Then
ReDim arrObj(0 To oSset.COUNT - 1) As ACADOBJECT
'Process each object
i = 0
For Each oEnt In oSset
Set arrObj(i) = oEnt
i = i + 1
Next
End If
'kills also left over selectionset by programming mistakes....
For Each selectionset In ThisDrawing.SelectionSets
selectionset.delete_by_layer_space
Next
On Error GoTo Err_Control
'Get an extension dictionary and, if necessary, add a SortentsTable object
Dim eDictionary As Object
Set eDictionary = ThisDrawing.modelspace.GetExtensionDictionary
' Prevent failed GetObject calls from throwing an exception
On Error Resume Next
Dim sentityObj As Object
Set sentityObj = eDictionary.GetObject("ACAD_SORTENTS")
On Error GoTo 0
If sentityObj Is Nothing Then
' No SortentsTable object, so add one
Set sentityObj = eDictionary.AddObject("ACAD_SORTENTS", "AcDbSortentsTable")
End If
'Move selected object(s) to the top
sentityObj.MoveToTop arrObj
applicaTION.UPDATE
Exit Sub
Err_Control:
If ERR.NUMBER > 0 Then MsgBox ERR.DESCRIPTION
End Sub
Sub bringtofrontbylist()
Dim lnames As String
'predefined layer names
layer_names = "foundation bridge road"
Dim h() As String
h = split(layernames)
For i = 0 To UBound(h)
Call OrderToTop(h(i))
Next
End Sub
'in case you want a fancy form here is how to get list / all layers
Sub list_layers()
Dim LAYER As AcadLayer
For Each LAYER In ThisDrawing.LAYERS
Debug.Print LAYER.NAME
Next
End Sub
to make it run put the cursor inside the VBA IDE inside the code of list_layers andpress F5 or choose it from the VBA Macro list.
I'm having some trouble with looping and creating MS Excel docs, code snippet below
Private Sub selectedRowsButton_Click( _
ByVal sender As Object, ByVal e As System.EventArgs) _
Handles selectedRowsButton.Click
Dim selectedRowCount As Integer = _
DataGridView1.Rows.GetRowCount(DataGridViewElementStates.Selected)
If selectedRowCount > 0 Then
Dim sb As New System.Text.StringBuilder()
Dim objexcel As New Excel.Application
Dim i As Integer
Dim FACode As Integer
Dim Sitename As Integer
Dim Sitecode As Integer
Dim Address As Integer
Dim City As Integer
Dim State As Integer
Dim ZIP As Integer
FACode = 1
Sitename = 5
Sitecode = 2
Address = 6
City = 7
State = 9
ZIP = 10
Dim xlWorkbook As Excel.Workbook
xlWorkbook = objexcel.Workbooks.Open("template path")
For i = 0 To selectedRowCount - 1
objexcel.Visible = True
objexcel.Range("B2").Value = DataGridView1.SelectedCells(Sitename).Value.ToString()
objexcel.Range("B3").Value = DataGridView1.SelectedCells(Sitecode).Value.ToString()
objexcel.Range("B5").Value = DataGridView1.SelectedCells(FACode).Value.ToString()
Dim thisfile As Object
thisfile = objexcel.Range("B5").Value & "." & _
objexcel.Range("B3").Value & "." & "otherstring" & "." & "otherstring2" & "." & ".xls"
With objexcel
xlWorkbook.SaveAs(Filename:="c:\test\" & thisfile)
'~~> Close the Excel file without saving
xlWorkbook.Close(False)
End With
Next i
End If
I'm getting the error Exception from HRESULT: 0x800A03EC for the statement
objexcel.Range("B2").Value = DataGridView1.SelectedCells(Sitename).Value.ToString()
IF I select only one row of my DataGrid before creating the program works fine, it is when I select multiple rows that this error occurs. Since I'm creating the program specifically for multiple row selections I'm stumped as to where I've gone wrong. Any help or pointers appreciated, Thanks!
Two things
You have declared objexcel As Excel.Application so you shouldn't use objexcel.Range("B2").Value. Use xlWorkbook.Range("B2").Value. Change it everywhere in your code.
You cannot use SaveAs like that. See the snapshot below. If you want to save as xls file then you have to use FileFormat:=56
See this code example
'~~> Save As file
xlWorkbook.SaveAs(Filename:="c:\test\" & thisfile, FileFormat:=56)
If you do not specify the file format then you will get an error message when you open the file after opening.
You might want to look at this link on how to automate Excel from VB.Net
Topic: VB.NET and Excel
Link: http://www.siddharthrout.com/vb-dot-net-and-excel/
I am not too sure what you exactly are trying to do with the DGV. Like Sean mentioned you are not incrementing the values. If you can post a snapshot of how your DGV looks and how your Excel file should look after the export then we can help you in a much better way :)
I created a VB.Net application that will read from excel file and put the data into a table.
I used an excel sheet which has 3 columns and 65000 rows.
Before starts reading the excel my machine's CPU Usage is around 15%, but during reading the CPU Usage jumps upto 95%.
I don't know why it is happening? Can someone help me in this issue?
The following is the code i'd written:
Private Sub readFromExcel(ByVal fileName As String, ByVal sheetName As String)
Dim connString As String = "data source=XE; user=test; password=test"
Dim con As New OracleConnection(connString)
Dim str1 As String
Dim str2 As String
Dim str3 As String
Dim xlApp As Excel.Application
Dim xlWorkBook As Excel.Workbook
Dim xlWorkSheet As Excel.Worksheet
xlApp = New Excel.ApplicationClass
xlWorkBook = xlApp.Workbooks.Open(fileName)
xlWorkSheet = xlWorkBook.Worksheets(sheetName)
Dim x As Integer
Dim y As Integer
Dim i As Integer
x = xlWorkSheet.Rows.Count()
y = xlWorkSheet.Columns.Count()
Try
For i = 1 To x - 1
'MsgBox(xlWorkSheet.Cells(i, 0).value)
str1 = xlWorkSheet.Cells(i, 1).value
str2 = xlWorkSheet.Cells(i, 2).value
str3 = xlWorkSheet.Cells(i, 3).value
insertData()
Next
Catch ex As Exception
MsgBox(ex.Message())
Finally
con.Close()
xlWorkBook.Close()
End Try
End Sub
Private Sub insertData()
Dim str As String
str = "insert into test_import values('" + str1 + "'," + str2 + "," + str3 + ")"
Dim cmd As New OracleCommand()
cmd.CommandText = str
cmd.Connection = con
cmd.ExecuteNonQuery()
End Sub
thx in advance.
This is entirely normal. A program only doesn't burn 100% core when it gets bogged down by I/O. Reading from a disk or network card, that blocks a program while the operating system supplies the data. Your code doesn't bog down like that, you are asking it to do a bunch of work. Getting 195,000 cell values one by one just takes a while. Excel is an out-of-process COM server so every cell read requires two CPU context switches. You can optimize it a bit by using a Range instead. Or by running it on a machine with a two-core CPU so it only shoots up to 50%.
Feature, not a bug.
Its much faster to read the 195000 cells into an Object array in one go and then loop the object array. (There is a very high overhead for each .Net call to the Excel object model)
Use get_range(cell1,cell2) method to get the cells value.
You can use it to take the cell value by row, by column, or take all cells value in one go.
Keep watch the CPU usage when you adjust the code to read the cell value either by row, by column, or take all cells value in one go.
I have absolutely no experience programming in excel vba other than I wrote a function to add a data stamp to a barcode that was scanned in on our production line a few weeks back, mainly through trial and error.
Anyways, what I need help with right now is inventory is coming up and every item we have has a barcode and is usually scanned into notepad and then manually pulled into excel and "text to columns" is used. I found the excel split function and would like a little bit of help getting it to work with my scanned barcodes.
The data comes in in the format: 11111*A153333*11/30/11 plus a carriage return , where the * would be the delimiter. All the examples I've found don't seem to do anything, at all.
For example here is one I found on splitting at the " ", but nothing happens if I change it to *.
Sub splitText()
'splits Text active cell using * char as separator
Dim splitVals As Variant
Dim totalVals As Long
splitVals = Split(ActiveCell.Value, "*")
totalVals = UBound(splitVals)
Range(Cells(ActiveCell.Row, ActiveCell.Column + 1), Cells(ActiveCell.Row, ActiveCell.Column + 1 + totalVals)).Value = splitVals
End Sub
And this is applied in the Sheet1 code section, if that helps.
It really can't be this complicated, can it?
Edit: Trying to add in Vlookup to the vba.
So as I said below in the comments, I'm now working on getting the vlookup integrated into this, however it just returns N/A.
Here is the sub I wrote based on the link below
Public Sub vlook(ByRef codeCell As Range)
Dim result As String
Dim source As Worksheet
Dim destination As Worksheet
Set destination = ActiveWorkbook.Sheets("Inventory")
Set source = ActiveWorkbook.Sheets("Descriptions")
result = [Vlookup(destination!(codeCell.Row, D), source!A2:B1397, 2, FALSE)]
End Sub
And I was trying to call it right after the For loop in the worksheet change, and just created another for loop, does this/should this be a nested for loop?
Just adding the code to the VBA behind the worksheet won't actually cause it to get called. You need to handle the worksheet_change event. The following should help:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
Dim cell As Range
For Each cell In Target.Cells
If cell.Column = 1 Then SplitText cell
Next
Application.EnableEvents = True
End Sub
Public Sub SplitText(ByRef codeCell As Range)
'splits Text active cell using * char as separator
Dim splitVals As Variant
Dim totalVals As Long
splitVals = Split(codeCell.Value, "*")
totalVals = UBound(splitVals)
Range(Cells(codeCell.Row, codeCell.Column), Cells(codeCell.Row, codeCell.Column + totalVals)).Value = splitVals
End Sub
If you want to process the barcodes automatically on entering them, you need something like this (goes in the worksheet module).
Private Sub Worksheet_Change(ByVal Target As Range)
Dim splitVals As Variant
Dim c As Range, val As String
For Each c In Target.Cells
If c.Column = 1 Then 'optional: only process barcodes if in ColA
val = Trim(c.Value)
If InStr(val, "*") > 0 Then
splitVals = Split(val, "*")
c.Offset(0, 1).Resize( _
1, (UBound(splitVals) - LBound(splitVals)) + 1 _
).Value = splitVals
End If
End If 'in ColA
Next c
End Sub
I am updating an old VB6 app. Back in the day, I coded a wrapper around the mciSendString command to be able to record and playback audio. Back then, computers typically had a single audio card.
Now, many of the customers have multiple sound cards (typically a built in one and a USB headset).
I can't seem to find the API to specify which sound card to use with mciSendString. Can someone point me in the right direction?
Please check out several solutions here with sourcecode (Ergebnisse des Wettbewerbs / German):
http://www.activevb.de/rubriken/wettbewerbe/2009_september/comp_2009_september_mp3_player.html
Here some source of my project
''' <summary>
''' Return all audio devices by names
''' </summary>
Private Function ListSoundDevices(ByRef LDev As List(Of String)) As Boolean
Dim intItem As New Integer
Dim intNext As New Integer
Dim intCount As New Integer
Dim tWIC As New WaveInCaps
Dim Enc As System.Text.ASCIIEncoding = New System.Text.ASCIIEncoding()
Dim res As Boolean = False
Try
'Throw New Exception("test")
intCount = waveInGetNumDevs()
Catch ex As Exception
'no devices found
Return False
End Try
If intCount <> 0 Then
For intItem = 0 To intCount - 1
If waveInGetDevCaps(intItem, tWIC, Marshal.SizeOf(tWIC)) = MMSYSERR_NOERROR Then
If (tWIC.Formats And WAVE_FORMAT_4S16) = WAVE_FORMAT_4S16 Then
If LDev Is Nothing Then LDev = New List(Of String)
Dim B() As Byte = System.Text.Encoding.Default.GetBytes(tWIC.ProductName.ToString.ToCharArray)
LDev.Add(Enc.GetString(B, 0, B.Length))
intNext += 1
End If
End If
Next
If intNext > 0 Then res = True
End If
Return res
End Function
Use the device ID to start record and using.
Hope that helps
Microsoft has provided the answer.
To set the WaveAudio device (soundcard) used by the Multimedia Control, you must use the mciSendCommand API. The Multimedia Control does not directly provide a method to let you set the device used for playing or recording.
Dim parms As MCI_WAVE_SET_PARMS
Dim rc As Long
' Specify the soundcard. This specifies the soundcard with a deviceID
' of 0. If you have a single soundcard, then this will open it. If you
' have multiple soundcards, the deviceIDs will be 0, 1, 2, etc.
parms.wOutput = 0
' Send the MCI command to set the output device.
rc = mciSendCommand(MMControl1.DeviceID, MCI_SET, MCI_WAVE_OUTPUT, parms)