Adding a reference for any user - vba

I am struggling with references in my VBA project. In order to use my Drag and Drop function, I need to add the following reference to my workbook: Microsoft Windows Common Controls 6.0 (SP6). This reference has got the following location : C:\Windows\system32\MSCOMCTL.OCX.
I would like to make sure that for any user using my specific workbook, the reference won't be missing. I have seen many tutorials about it and could "easily" add a reference, but I would like to understand precisely what I am doing and choose the best solution. I have several questions:
Late binding, Early Biding, AddFromFile, AddFromGUI
In those two topics (1 and 2), I read many things about references. The problem is I really do not know which option fits the best to my particular case.
My reference has already been added manually. Should I code something in order to be sure it is not missing when the code runs?
About adding a reference : Which option between those four is the best? I am hesitating between AddFromFile and Late Binding.
Let's suppose I choose AddFromFile or Late Binding. I cannot manage to Print my reference's name! How do you know that Microsoft VBScript Regular Expressions 5.5's .Name is VBScript_RegExp_55? I tried the code in this topic but it does not work (Must have done something wrong...).
"Universal" Path to my reference
I am very pessimistic and I was wondering: are references always stored at the same location? Let's suppose a user uses my specific workbook and hasn't got the reference it needs for the Drag and Drop function. He/She would need to add MSCOMCTL.OCX from C:\Windows\system32\.
Are references always stored at the same location?
If not, how can I overcome that? Changing the path to something like the following?
Sub mySub()
Dim sRef, sPath As String
sRef = "MSCOMCTL.OCX"
sPath = Environ("Windir") & "\system32\" & sRef
End Sub
Thank you in advance for your answers and sorry if things are crystal clear and already answered in other posts, I may have missed it.

Related

SolidWorks VBA - Translating API Help into useable code

I'd like to do what feels like a fairly simple task, and I've found the specific API Help pages which should make it clear, but, I can't actually make things work.
The Key steps that I would like to achieve are:
Rename the active document
Update References to this document to accommodate new name
Save active document.
This help page shows the Usage for renaming the doc, and under the "Remarks" heading, includes links to the next two steps, mentioning them off hand as if implementing them would be easy.
https://help.solidworks.com/2020/English/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IModelDocExtension~RenameDocument.html?verRedirect=1
The trouble is, I'm a bit of a VBA beginner - usually I get by with the 'record' function, and then tidying things up from there - but undertaking the steps above manually doesn't result in anything being recorded at all for one reason or another.
Assuming I am able to pass in the item to be renamed (I'll define a variable at the start of the Sub for this e.g. swModel = swApp.ActiveDoc), and the new name (NewName = "NEW NAME HERE"), How would I translate the Help API into a Sub that I can actually run?
Two of them suggest declaring as a Function, and one as a Public Interface - I've never used these before - do these just run in a standard Module? Do I need to write a 'master Sub' to call the different functions sequentially, or could these be included directly in the sub, if they're only to be used once?
[Feeling a little lost - it's demoralizing when the help files aren't all that helpful]
Let me know if there's any more information missing that I can add to improve my question - as I said, I'm fairly new to this coding thing...
The "record" function is sometimes a good point to start but there are a lot of functions it can't recognize while you execute them manually.
The API Help is then useful to find out how to use a specific function.
In almost every example the use of a specific method (e.g. RenameDocument) is only shown abstract. There is always a instance variable which shows you the object-type needed to call this method. So you can use these in every sub you want, but beforehand need access to the specific instance objects.
For your example the RenameDocument method is called with an object of the type IModelDocExtension. First thing for you to do is to get this object and then you can call the method as described in the help article.
Under Remarks in the article you find additional information for what you maybe have to do before or after calling a method.
For your example it is mentioned that the renaming takes permanently place after saving the document.
And finally here is what you want to do with some VBA code:
Dim swApp As SldWorks.SldWorks
Dim swModel As ModelDoc2
Sub main()
' get the solidworks application object
Set swApp = Application.SldWorks
'get the current opened document object
Set swModel = swApp.ActiveDoc
' get the modeldocextension object
Dim swModelExtension As ModelDocExtension
Set swModelExtension = swModel.Extension
Dim lRet As Long
lRet = swModelExtension.RenameDocument("NEW NAME")
If lRet = swRenameDocumentError_e.swRenameDocumentError_None Then
MsgBox "success renaming"
Else
MsgBox "failed with error: " & lRet
End If
End Sub
Afterwars you have to process the return value to check for errors described in this article: https://help.solidworks.com/2020/English/api/swconst/SolidWorks.Interop.swconst~SolidWorks.Interop.swconst.swRenameDocumentError_e.html

Remove namespace or classname from VB.Net when used in VBA [duplicate]

Base Reference: Ten Code Conversions for VBA, Visual Basic .NET, and C#
Note: I have already created and imported a *.dll, this question is about aliases.
Let's say the programmatic name of a Test class is TestNameSpace.Test
[ProgId("TestNamespace.Test")]
public class Test ...
Now, say a C# solution has been sealed and compiled into a *.dll and I'm referencing it in a Excel's VBE. Note: at this point I cannot modify the programmatic name as if the *.dll wasn't written by me.
This is in VBA : Instead of declaring a variable like this:
Dim myTest As TestNameSpace.Test
Set myTest = new TestNameSpace.Test
I'd prefer to call it (still in VBE)
Dim myTest As Test
Set myText = new Test
In C# you would normally say
using newNameForTest = TestNamespace.Test;
newNameForTest myTest = new NewNameForTest;
Note: Assume there are no namespace conflicts in the VBA project
Question: is there an equivalent call in VBA to C# using or VB.NET imports aliases?
Interesting question (constantly using them but never thought about their exact meaning). The definition of the Imports statement (same for using) is pretty clear: its only function is shortening the references by removing the corresponding namespaces. Thus, the first question to ask is: has VBA such a thing (namespaces) at all? And the answer is no, as you can read from multiple sources; examples: Link 1 Link 2
In summary, after not having found a single reference to any VBA statement doing something similar to Imports/using and having confirmed that VBA does not consider the "structure" justifying their use (namespaces), I think that I am in a position to say: no, there is not such a thing in VBA.
Additionally you should bear in mind that it wouldn't have any real applicability. For example: when converting a VB.NET code where Imports might be used, like:
Imports Microsoft.Office.Interop.Word
...
Dim wdApp As Application
the code would be changed completely, such that the resulting string will not be so long:
Dim wdApp As Word.Application ' Prefacing the library's display name.
I think that this is a good graphical reason explaining why VBA does not need to have this kind of things: VB.NET accounts for a wide variety of realities which have to be properly classified (namespaces); VBA accounts for a much smaller number of situations and thus can afford to not perform a so systematic, long-named classification.
-------------------------- CLARIFICATION
Imports/using is a mere name shortening, that is, instead of writing whatever.whatever2.whatever3 every time you use an object of the given namespace in a Module/ Class, you add an Imports/using statement at the start which, basically, means: "for all the members of the namespace X, just forget about all the heading bla, bla".
I am not saying that you cannot emulate this kind of behaviour; just highlighting that having an in-built functionality to short names makes sense in VB.NET, where the names can become really long, but not so much in VBA.
The answer is no: there is a built-in VBE feature that recognizes the references added to a project and creates aliases at run-time(VBE's runtime) if there are no name collisions
In case of name conflicts in your registry all . dots will be replaces with _ underscores.
» ProgId's (Programmatic Identifiers)
In COM, it is only used in late-binding. It's how you make a call to create a new object
Dim myObj = CreateObject("TestNamespace.Test")
» EarlyBinding and LateBinding
In early binding you specify the type of object you are creating by using the new keyword. The name of you object should pop up with the VBA's intellisense. It has nothing to do with the ProgId. To retrieve the actual namespace used for your object type - open Object Explorer F2 and locate it there
This article explain where the names come from in Early Binding Section
use the same link for When to use late binding
for MSDN Programmatic Identifiers section please see this

convert early binding to late binding without changing object type

This seems like a simple question but I after chasing forums for several hours I think it might be impossible.
I often want to convert a program from early binding to late binding. Usually, it is a vba, visual basic for applications, program that runs under Excel 2010 and windows 7 pro.
For discussion purposes, let’s pretend it is the following.
Sub EarlyBind()
' use IDE > Tools > references > and select “Microsoft Internet Controls”
Dim shellWins1 as shdocvw.shellwindows
Line1: Set shellWins1 = New SHDocVw.ShellWindows
MsgBox TypeName(shellWins1) ' this will display “IShellWindows”
' other code that expects to be working with an IshellWindows object …..
End Sub
In my experience, converting such a program to late binding is sometimes hard.
For instance, I found some forums that suggest I change it to
Set shellwins1 = createobject("Shell.applicaton")
But that creates a IShellDispatch5 object, not an IshellWindows object. That means I have to change other code to accommodate the new object type. And, of course I have to test that other code for subtle differences.
So, my goal is to find a general solution that will allow me to rewrite “Line1” to create the CORRECT object type with late binding. I also wish to avoid the need setting a reference to "Microsof Internet Controls. In other words, I want the code to look like this:
Sub LateBind()
Dim shellWins1 as object
Line1: Set shellWins1 = createobject(“xxxxxx.yyyyyy”).zzzzzz
MsgBox TypeName(shellWins1) ‘ this should display “IShellWindows”
….. other code that expects to be working with an IshellWindows object …..
End Sub
I know how to use the vba IDE to find the dll associated with the object. In this case the dll is Library SHDocVw C:\Windows\SysWOW64\ieframe.dll.
I have installed OleView and can find the associated IshellWindows “magic numbers” for the clsId, TypeLib, and Inteface (for instance the interface is 85CB6900-4D95-11CF-960C-0080C7F4EE85).
But, I don’t know how to convert them into a program id that can be used in line1 in the sample code posted above.
I hope someone here can help.
------ With MeHow's help, I now have the answer! ------
To switch 'set myObj = new xxxx.yyyyy' to late binding for arbitrary object types
Change set myObj = new xxxx.yyyyy
into set myObj = CreateObject("xxxx.yyyyy")
Very often that will work.
But, in the some cases, (e.g. "shDocVw.ShellWindows.") it gives error 429 ActiveX component cannot be created.
When that occurs I AM COMPLETELY OUT OF LUCK. It is impossible to use late binding with that EXACT object class. Instead I must find a substitute class that does approximately the same thing. (e.g. "Shell.Application").
Your short answer is
IShellWindows is an interface.
It
Provides access to the collection of open Shell windows.
Therefore
Take a look at the CreateObject() method.
Note:
Creates and returns a reference to a COM object. CreateObject cannot
be used to create instances of classes in Visual Basic unless those
classes are explicitly exposed as COM components.
IShellWindows is not exposed as a COM component so that's why there is no way to say CreateObject("SHDocVw.IShellWindows")
When you open your registry (regedit) and search for a key type in IShellWindows. If you find anything that means you've found your Prog ID and if you don't find anything it means that nothing like IShellWindows is registered as a prog Id therefore it would make sense to assume that you can't late bind IShellWindows
I bumped into your question trying to find something for myself. But I don't know if you have tried the following -
Set shellwins1 = createobject("Shell.Application")
MsgBox TypeName(shellWins1.Windows)
This answers your question for datatype. It prints IShellWindows for me. I'm not sure though if it could actually solve your purpose for latebinding meaning if this would be the object required though the datatype is what you need.
So, I would advise you to give it a try.
There is a slightly better approach outlined at https://www.experts-exchange.com/questions/28961564/How-to-find-the-class-id-of-an-arbitrary-object-Example-Set-x-CreateObject-New-1C3B4210-F441-11CE-B9EA-00AA006B1A69.html#a41743468.

How to set user "logon to" AD attribute in VB.NET

I'm working on upgrading a solution in VB.NET that is heavily based on Active Directory. As of now, I'm trying to add a PC restriction to a new AD User upon user creation. Essentially, I need to update the Logon To attribute to include 1 or more PCs, how do I go about doing this?
I learned that I am interested in the IADsUser property "LoginWorkstations" (thanks to http://msdn.microsoft.com/en-us/library/Aa746340). As of now, I have code that can fetch this attribute from any AD user, but I cannot set it.
Here is the code I have to fetch the attribute:
Dim userADObject As new DirectoryEntry(ADPath)
Dim logonToPC as String = userADObject.InvokeGet("LoginWorkstations")(0).ToString
That will fetch the first restricted PC (if there is one) and save it in logonToPC and will look something like "PC10000"
That works great, so intuitively I would assume something like this would work:
Dim userADObject As new DirectoryEntry(ADPath)
Dim args() As Object = {"PC100001"}
userADObject.InvokeSet("LoginWorkstations", args)
But it doesn't work... It just throws a rather unhelpful exception.
I've tried testing this approach with a different attribute and it works just fine. Not much out there on Google either unfortunately...
Any help would be greatly appreciated.
You should be able to do this fairly easily - also: note that you should use the userWorkstations LDAP attribute (see note here) - this is multi-valued, e.g. it allows multiple entries.
Dim userADObject As new DirectoryEntry(ADPath)
userADObject.Properties("userWorkstations").Add("PC001")
userADObject.Properties("userWorkstations").Add("PC002")
userADObject.Properties("userWorkstations").Add("PC003")
userADObject.CommitChanges()
If you have the necessary permissions to update Active Directory, that should basically do it, I think.
Found the solution that works. I took marc_s's code and modified a bit to work properly. here's what I have:
Dim userADObject As New DirectoryEntry(Me.ADPath)
'Grab the previous restriction, because we may have to clear it first in the future
Dim priorRestriction As String = userADObject.Properties("userWorkstations").Value
If priorRestriction = "" Then
'Simply add
userADObject.Properties("userWorkstations").Add("PC001,PC002")
Else
'Important - We have to clear the old restriction before adding the new
userADObject.Properties("userWorkstations").Remove(priorRestriction)
'Now add the new restriction
userADObject.Properties("userWorkstations").Add(priorRestriction & ",PC003")
End If
'Commit!
userADObject.CommitChanges()
Something that gave me some pretty good grief what that I you can't have a space in the string being added. Example: .Add("PC001, PC002") has to be .Add("PC001,PC002")

How do I use Google.GData.Client.AtomLinkCollection.FindService method to get the list of worksheets in a Google Spreadsheet?

I'm trying to write code that talks to Google Spreadsheets. We do a bunch of processing on our end and then pass data out to our client into this spreadsheet and I want to automate it. This seems like it should be easy.
On this page, Google says "Given a SpreadsheetEntry you've already retrieved, you can print a list of all worksheets in this spreadsheet as follows:"
AtomLink link = entry.Links.FindService(GDataSpreadsheetsNameTable.WorksheetRel, null);
WorksheetQuery query = new WorksheetQuery(link.HRef.ToString());
WorksheetFeed feed = service.Query(query);
foreach (WorksheetEntry worksheet in feed.Entries)
{
Console.WriteLine(worksheet.Title.Text);
}
Following along at home, I start with:
Dim link As AtomLink = Entry.Links.FindService(GDataSpreadsheetsNameTable.WorksheetRel, "")
Dim wsq As New WorksheetQuery(link.HRef.ToString)
and when execution gets to that second line, I find that "Object reference not set to instance of an object." The FindService method is returning nothing. And when I look at GDataSpreadsheetsNameTable.WorksheetRel, it's a constant value of "http://schemas.google.com/spreadsheets/2006#worksheetsfeed"
I'm not really at the point where I even grok what it wants to be doing. E.g., what's a feed? Is a worksheet really what I think it is based on Excel nomenclature? That kind of stuff. But I see a couple of things that might be causing my issue:
The C# method call "...FindService(GDataSpreadsheetsNameTable.WorksheetRel, null);" I'm not sure about that null. It demands a string, so I used "" in my VB, but I'm not sure that's right.
That schemas.google.com URI doesn't seem to be live. At least, if I punch it into a browser, I get server not found. But again, I don't exactly know what it's trying to do.
So, any thoughts? Anyone have VB code that reads Google Spreadsheets and time to instruct a newbie? I'm surprised to find that there's essentially no useful sample code floating around the net.
Thanks for reading!
So, of course, right after I posted this I found some inspiration over here. Manually iterating across the collections works just fine, even if it's not the preferred way to do this. I'm still keen to hear info from others related to this, so feel encouraged to help out even though I'm maybe over this one hurdle.
For Each Entry In mySprShFeed.Entries
If Entry.Title.Text = "spreadsheetNameSought" Then
For Each link As AtomLink In Entry.Links
If link.Rel = GDataSpreadsheetsNameTable.WorksheetRel Then
Dim wsf As WorksheetFeed = service.Query(New WorksheetQuery(link.HRef.ToString))
For Each worksheet In wsf.Entries
Console.WriteLine(worksheet.Title.Text)
Next
End If
Next
End If
Next