AddRef() in VB6 - com

is it possible to call method AddRef() from interface IUnknown in visual basic 6 or implement some hack to increment object reference counter?

If you are trying to artificially increment the refrence counter (outside of the automatic reference counting that is done by VB6) then you can do the following:
Public Sub AddOneToRefCount(target As Object)
Dim tempObj As Object
Set tempObj = target 'VB6 calls AddRef for you here
Call CopyMemory(ByVal ObjPtr(tempObj), 0&, 4&) 'trick VB into not calling Release
End Sub
You'll probably need to implement the reverse of this as well to release or you will get a memory leak of sorts.

I'm fairly sure that VB6 doesn't support directly accessing the IUnknown methods. But the workaround is simple. Add this declaration to a Module:
Dim ReferenceHolder As Variant
And then get AddRef() to get called with a simple assignment
Set ReferenceHolder = someObject
Reversely, get Release() to get called with
Set ReferenceHolder = Nothing
This is normal VB6 memory management at work.

Related

Can a CodeAnalysis return a false positive of CA2202? or is really something wrong with my code?

I'm suffering the same issue explained here but iterating a EnvDTE.Processes.
In the question that I linked the user #Plutonix affirms it is a false warning, and I think him reffers to the obj.Getenumerator() mention so I assume my problem will be considered a false warning too, however, if this is a false warning I would like to know more than an affirmation, the arguments to say it is a false warning.
This is the warning:
CA2202 Do not dispose objects multiple times Object
'procs.GetEnumerator()' can be disposed more than once in method
'DebugUtil.GetCurrentVisualStudioInstance()'. To avoid generating a
System.ObjectDisposedException you should not call Dispose more than
one time on an object.: Lines:
214 Elektro.Application.Debugging DebugUtil.vb 214
This is the code, procs object is the involved one on the warning, but I don't see any disposable object:
Public Shared Function GetCurrentVisualStudioInstance() As DTE2
Dim currentInstance As DTE2 = Nothing
Dim processName As String = Process.GetCurrentProcess.MainModule.FileName
Dim instances As IEnumerable(Of DTE2) = DebugUtil.GetVisualStudioInstances
Dim procs As EnvDTE.Processes
For Each instance As DTE2 In instances
procs = instance.Debugger.DebuggedProcesses
For Each p As EnvDTE.Process In procs
If (p.Name = processName) Then
currentInstance = instance
Exit For
End If
Next p
Next instance
Return currentInstance
End Function
PS: Note that the code-block depends on other members but they are unrelevant for this question.
Short version: this looks like a bug in the Code Analysis component to me.
Long version (hey, you suckered me into spending the better part of my afternoon and evening deciphering this, so you might as well spend a little time reading about it :) )…
The first thing I did was look at the IL. Contrary to my guess, it did not contain multiple calls to Dispose() on the same object. So much for that theory.
The method did, however, contain two separate calls to Dispose(), just on different objects. By this time, I was already convinced this was a bug. I've seen mention of CA2202 being triggered when dealing with related classes where one class instance "owns" an instance of the other class, and both instances are disposed. While inconvenient and worth suppressing, the warning seems valid in those cases; one of the objects really is getting disposed of twice.
But in this case, I had two separate IEnumerator objects; one did not own, nor was even related to, the other. Disposing one would not dispose the other. Thus, Code Analysis was wrong to warn about it. But what specifically was confusing it?
After much experimentation, I came up with this near-minimal code example:
Public Class A
Public ReadOnly Property B As B
Get
Return New B
End Get
End Property
End Class
Public Interface IB
Function GetEnumerator() As IEnumerator
End Interface
Public Class B : Implements IB
Public Iterator Function GetEnumerator() As IEnumerator Implements IB.GetEnumerator
Yield New C
End Function
End Class
Public Class C
Dim _value As String
Public Property Value As String
Get
Return _value
End Get
Set(value As String)
_value = value
End Set
End Property
End Class
Public Shared Function GetCurrentVisualStudioInstance2() As A
For Each a As A In GetAs()
For Each c As C In a.B
If (c.Value = Nothing) Then
Return a
End If
Next c
Next a
Return Nothing
End Function
Public Shared Iterator Function GetAs() As IEnumerable(Of A)
Yield New A()
End Function
This produces the same spurious CA2202 you are seeing in the other code example. Interestingly though, a minor change to the declaration and implementation of interface IB causes the warning to go away:
Public Interface IB : Inherits IEnumerable
End Interface
Public Class B : Implements IB
Public Iterator Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Yield New C
End Function
End Class
Somehow, Code Analysis is getting confused by the non-IEnumerable implementation of GetEnumerator(). (Even more weirdly is that the actual type you're using, the Processes interface in the DTE API, both inherits IEnumerable and declares its own GetEnumerator() method…but it's the latter that is the root of the confusion for Code Analysis, not the combination).
With that in hand, I tried to reproduce the issue in C# and found that I could not. I wrote a C# version that was structured exactly as the types and methods in the VB.NET version, but it passed Code Analysis without warnings. So I looked at the IL again.
I found that the C# compiler generates code very similar to, but not exactly the same as, the VB.NET compiler. In particular, for the try/finally blocks that protect the IEnumerator returned for each loop, all of the initialization for those loops is performed outside the try block, while in the VB.NET version the initialization is performed inside.
And apparently, that is also enough to prevent Code Analysis from getting confused about the usage of the disposable objects.
Given that it seems to be the combination of VB.NET's implementation of For Each and the nested loops, one work-around would be to just implement the method differently. I prefer LINQ syntax anyway, and here is a LINQified version of your method that compiles without the Code Analysis warning:
Public Shared Function GetCurrentVisualStudioInstance() As DTE2
Dim processName As String = Process.GetCurrentProcess.MainModule.FileName
Return GetVisualStudioInstances.FirstOrDefault(
Function(instance)
Return instance.Debugger.DebuggedProcesses.Cast(Of EnvDTE.Process).Any(
Function(p)
Return p.Name = processName
End Function)
End Function)
End Function
And for completeness, the C# version (since all of this code started when a C# implementation was converted to VB.NET and then extended to handle the "current instance" case):
public static DTE2 GetCurrentVisualStudioInstance()
{
string processName = Process.GetCurrentProcess().MainModule.FileName;
return GetVisualStudioInstances()
.FirstOrDefault(i => i.Debugger.DebuggedProcesses
.Cast<EnvDTE.Process>().Any(p => p.Name == processName));
}

Convert VB.net Code into C#.net

I am working on a C#.net. I have a code of vb.net and I want to convert those code into C#.
I have done all my task but while running application it gives me a error of object not set to an object reference. below is my VB.net code. I have used third party ddl in my code so ExchangeList is a class of that dll.
Private WithEvents moExchanges As ExchangeList
Private Sub RequestChartData()
Trace.WriteLine("Init")
moExchanges = moHost.MarketData.Exchanges
End Sub
Now below is my C#.code
Private Host moHost;
Private ExchangeList moExchanges;
private void RequestChartData()
{
Trace.WriteLine("Init");
moExchanges = moHost.MarketData.Exchanges;
}
Thanks.
It's not possible to tell you with 100% certainty the source of your object not set to an object reference. without seeing more code, but the error appears to be telling you that moHost is null - in other words, you haven't created an instance of the object yet.
So when you try to call MarketData.Exchanges on the object (so you can assign it to moExchanges, it's throwing the error.
Find moHost in your code and make sure you have that you have assigned an instance of that object to it (by calling it's constructor, like moHost = new Host() or whatever the constructor is), and this should fix your error.
UPDATE
You never initialize moHost. This line:
private Host moHost;
Simply declares the object - at this point it's null. So when you try to access any instance methods/properties, you get the object not set to an object reference error.
You need to create an instance of moHost by calling it's constructor, something like this:
private Host moHost = new Host();
The constructor may require parameters - take a look at the documentation for the third-party DLL (intellisense in the IDE may also tell you what parameters, if any, are needed).
This will take care of your error.
()Instead of:
Private Host moHost;
Private ExchangeList moExchanges;
I think you need to write it as:
Private Host moHost = New Host();
Private ExchangeList moExchanges = New ExhangeList();

Copy VB.NET list object to another list object

I am having a problem when setting VB.NET list object to another. In the example below
I create an instance on ReadLocations and than create an object of ReadLocation where then I loop through ReadLocations and set ReadLocation1 equal to rl.
What happens is that if I then go and change ReadLocation1 to something else (assdfhsd) it will also change the ReadLocations index. I am really confused why it would be doing that unless it is "=" sign means referencing instead of actually setting the value. Please help as I am a C# developer but the program I am modifying is in VB.NET.
Dim ReadLocations As New List(Of Model.ReadLocation)
Dim rl1 As New Model.ReadLocation
rl1.LL = "a"
Dim rl2 As New Model.ReadLocation
rl2.LL = "b"
ReadLocations.Add(rl1)
ReadLocations.Add(rl2)
Dim ReadLocation11 As New Model.ReadLocation
For Each rl As Model.ReadLocation In ReadLocations
ReadLocation11 = rl
Next
If ReadLocation is a reference type (a Class), then all variables set to instances of objects of that class will always be references. The = operator only ever sets a new reference to an object when it is operating on reference types. It will never make a clone of the object (unless it is a value type). The same is true in C#. The only way to do what you want to do, would be to clone the objects when you add them to the second list. Unfortunately, .NET doesn't provide a simple automatic method for cloning any object. The standard way to do this would be to implement the ICloneable interface in your ReadLocation class. Then you could clone it by calling the clone method:
ReadLocation1 = CType(rl.Clone(), ReadLocation)
However, inside that clone method, you will need to create a new instance of ReadLocation and manually set all of its properties and then return it. For example:
Public Class ReadLocation
Implements ICloneable
Public Function Clone() As Object Implements ICloneable.Clone
Dim clone As New ReadLocation()
clone.Property1 = Me.Property1
clone.Property2 = Me.Property2
Return clone
End Function

Autovivified properties?

suppose I declare a class like this:
Class tst
Public Props As New Dictionary(Of String, MyProp)
End Class
and added properties something along these lines:
Dim t As New tst
t.Props.Add("Source", new MyProp(3))
but now want to access it like this:
t.Source
how can I create a getter without knowing the name of the getter?
Ok, if you insist on "auto-vivifying", the only way I know of to do something like that is to generate the code as a string, and then compile it at runtime using the classes in the System.CodeDom.Compiler namespace. I've only ever used it to generate complete classes from scratch, so I don't know if you could even get it to work for what need to add properties to an already existing class, but perhaps you could if you compiled extension methods at runtime.
The .NET framework includes multiple implementations of the CodeDomeProvider class, one for each language. You will most likely be interested in the Microsoft.VisualBasic.VBCodeProvider class.
First, you'll need to create a CompilerParameters object. You'll want to fill its ReferencedAssemblies collection property with a list of all the libraries your generated code will need to reference. Set the GenerateExecutable property to False. Set GenerateInMemory to True.
Next, you'll need to create a string with the source code you want to compile. Then, call CompileAssemblyFromSource, passing it the CompilerParameters object and the string of source code.
The CompileAssemblyFromSource method will return a CompilerResults object. The Errors collection contains a list of compile errors, if there are any, and the CompiledAssembly property will be a reference to your compiled library (as an Assembly object). To create an instance of your dynamically compiled class, call the CompiledAssembly.CreateInstance method.
If you're just generating a small amount of code, it's pretty quick to compile it. But if it's a lot of code, you may notice an impact on performance.
Here's a simple example of how to generate a dynamic class containing a single dynamic property:
Option Strict Off
Imports System.CodeDom.Compiler
Imports Microsoft.VisualBasic
Imports System.Text
Public Class Form3
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim code As StringBuilder = New StringBuilder()
code.AppendLine("Namespace MyDynamicNamespace")
code.AppendLine(" Public Class MyDynamicClass")
code.AppendLine(" Public ReadOnly Property WelcomeMessage() As String")
code.AppendLine(" Get")
code.AppendLine(" Return ""Hello World""")
code.AppendLine(" End Get")
code.AppendLine(" End Property")
code.AppendLine(" End Class")
code.AppendLine("End Namespace")
Dim myDynamicObject As Object = generateObject(code.ToString(), "MyDynamicNamespace.MyDynamicClass")
MessageBox.Show(myDynamicObject.WelcomeMessage)
End Sub
Private Function generateObject(ByVal code As String, ByVal typeName As String) As Object
Dim parameters As CompilerParameters = New CompilerParameters()
parameters.ReferencedAssemblies.Add("System.dll")
parameters.GenerateInMemory = True
parameters.GenerateExecutable = False
Dim provider As VBCodeProvider = New VBCodeProvider()
Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, code)
If results.Errors.HasErrors Then
Throw New Exception("Failed to compile dynamic class")
End If
Return results.CompiledAssembly.CreateInstance(typeName)
End Function
End Class
Note, I never use Option Strict Off, but for the sake of simplicity in this example, I turned it off so I could simply call myDynamicObject.WelcomeMessage without writing all the reflection code myself.
Calling methods on objects using reflection can be painful and dangerous. Therefore, it can be helpful to provide a base class or interface in a shared assembly which is referenced by both the generated assembly, and the fixed assembly which calls the generated assembly. That way, you can use the dynamically generated objects through a strongly typed interface.
I figured based on your question that you were just more used to dynamic languages like JavaScript, so you were just thinking of a solution using the wrong mindset, not that you really needed to or even should be doing it this way. But, it is definitely useful in some situations to know how to do this in .NET. It's definitely not something you want to be doing on a regular basis, but, if you need to support custom scripts to perform complex validation or data transformations, something like this can be very useful.

How to make Activator.CreateInstance threaded

I have some code that runs within a multithreaded class.
Basically i create many threads which contain instance of this class.
Within this class i create an object to either a vb6 or .net dll using reflection, then i call a method within that object. The object that gets called can be different every time.
But for some reason even through i have multiple threads going when i get to the Activator.CreateInstance it is no longer multithreaded. Why is that? How do i fix it?
I want to catch the error returned and put it in a log.
If UCase(pRow("TypeVB6").ToString()) = "TRUE" Then
classType = Type.GetTypeFromProgID(ClasstoInstantiate, True)
Else
classType = Type.GetType(ClasstoInstantiate, True)
End If
Dim o As Object = Activator.CreateInstance(classType)
cError = classType.InvokeMember(MethodName, BindingFlags.InvokeMethod Or BindingFlags.Instance Or BindingFlags.Public, Nothing, o, New Object() {FilePath, VirtDir})
A couple of ideas for a solution:
1) Verify that the main entry point for your service is decorated with the MTAThread Attribute
2) Wrap the Activator.CreateInstance call (and possibly the following InvokeMember method) in a SyncLock statement.