Issue with VSIX VCProjectEngine Assembly dependency in Vs2012
-
Saturday, August 25, 2012 8:45 AM
Hello, we have a VSIX that is built with VS2010,
In this VSIX we reference Microsoft.VisualStudio.VCProjectEngine, Version=10.0.0.0 to handle some events from VC Projects.
When this VSIX is setup to VS2012, as we allow this, in a box without VS2010 setup we receive a FileNotFoundException for above assembly.
I think that if we reference VCProjectEngine version from Vs2012 and do nothing more we will have same problem in the reverse way, on a box with VS2010 standalone a FileNotFoundException will raise.
How can we get rid of this problem?
Might be possible to redistribute this assembly, (and related), with our VSIX?
Thank you so much in advance
jesus
All Replies
-
Saturday, August 25, 2012 6:40 PMI believe this is the type of problem that the EmbedInteropTypes setting is intended to solve.
-
Monday, August 27, 2012 8:19 AMModerator
Hi Jesus,
A FileNotFoundException was reported in your project. Are there some projects cann't be load via VCProjectEngine. Can we catch the exception message when we debug our vsix project?
I think some project files can't be found caused your issue.
Best regards,
Ego [MSFT]
MSDN Community Support | Feedback to us
-
Thursday, August 30, 2012 9:06 AM
Hello Ego,
If the issue is related to VCProjectEngine problem is related to the assembly dependency explained in the firdt thread post.
This error happens, with current pre-release (4.0.0.46), only when you have Visual Studio 2012 and you do not have Visual Studio 2010 setup in same machine.
The only solution for current release is to setup Visual Studio 2010 version together with Visual Studio 2012.
If your scenario is not this one, you have both Visual Studio 2012 and 2010 setup in your computer, or you are using Visual Studio 2010, and error happens anyway, then is another issue and we'll want investigate a bit.
please send an email to me so we can track what the issue is, jesus.salas A.T. vsanywhere D.O.T. com
We are working on a solution for this that is currently under test, hope next release solve completely this problem
jesus
-
Thursday, August 30, 2012 3:10 PMHave you looked into the use of the "Embed Interop Types" property on the VCProjectEngine reference? My understanding is that this is intended to solve (at least part) of the issue you describe, if I am properly understanding. For instance, if you reference the version of the VCProjectEngine interop assembly from VS2012 and set "Embed Interop Types" to True and then deploy to a VS2010-only machine, then the actual interop assembly does not need to exist on the target machine. If "Embed Interop Types" is False, then the runtime will attempt to load that assembly and fail, as you've seen.
-
Thursday, August 30, 2012 5:15 PM
Hello Pacific,
Is a bit more complex than that, both VCProjectEngine for vs2010 and vs2012 have exactly the same name, what disallow you to reference both assemblies in the project, also the instance for the vc engine is get using a dynamic object, so everything is really late bind at runtime, strange situation...
We have implement a workaround using reflection to hook everything and it is under test, preliminary test seems this can solve the issue, we used a similar technique to integrate with a third party Visual Studio plugin and it worked great---
Thank you so much for your feedback and proposed solution,
jesus
-
Thursday, August 30, 2012 6:17 PMI'm not suggesting you have references to two different VCProjectEngine assemblies. I'm suggesting exactly the opposite -- reference whichever one is on your dev machine and turn "Embed Interop Types" on so that the exact version you build against will get loaded -- irrespective of which version of VS is on the machine where you deploy. I'd be pretty afraid of any late-bound, reflection-based solution. I'm still not understanding why just turning on that setting doesn't solve your issue. Maybe an MS person can clarify for us -- if "Embed Interop Types" isn't for this kind of scenario, then that exactly is it for??
- Marked As Answer by VS Anywhere Thursday, August 30, 2012 8:57 PM
- Unmarked As Answer by VS Anywhere Thursday, August 30, 2012 8:57 PM
-
Thursday, August 30, 2012 8:57 PM
Hello again,
Look at this:
Microsoft.VisualStudio.VCProjectEngine, Version=10.0.0.0
[ComImport, TypeLibType(TypeLibTypeFlags.FDispatchable | TypeLibTypeFlags.FDual), Guid("69CE6B5F-30CB-435F-B9E2-9EA58AD37EEF")]
public interface _VCProjectEngineEvents
{
}
[ComImport, Guid("83F6153C-022F-444B-84C9-752B00F29800"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch), TypeLibType(TypeLibTypeFlags.FDispatchable)]
public interface _dispVCProjectEngineEvents
{
.....
}
Microsoft.VisualStudio.VCProjectEngine, Version=11.0.0.0
[ComImport, TypeLibType(TypeLibTypeFlags.FDispatchable | TypeLibTypeFlags.FDual), Guid("9DF4646B-52B9-4D04-8F03-6E88F2726235")]
public interface _VCProjectEngineEvents
{
}
[ComImport, TypeLibType(TypeLibTypeFlags.FDispatchable), InterfaceType(ComInterfaceType.InterfaceIsIDispatch), Guid("7BE8201E-F7DE-4821-954A-30904F16F83A")]
public interface _dispVCProjectEngineEvents
{
...
}
Same classes, same assembly name, same namespaces, (10.0 = VS2010, 11.00=VS2012), however they have different COM Guids defined
You manage to get an instance to VCProjectEngine, however, depending in the assembly reference you have in your project with the embed interop type or not, guids for COM objects will not match in one of the two Visual Studio Versions, (even if they get effectively embedded), so if you reference VS2010 Assembly (+embed interop or not), you will never get the right COM Object in VS2012 and vice-versa, (if you do not embed the interop type you get a strong reference to the assembly so only one will load and use by code)
For some reason there is no compatibility between interfaces, so the only way I can manage to solve this issue is going trough IDispatch, (dynamic type in c# 4.0), that is late-binding as we use to do in old days, nothing new...,
Hope these details give you a better understanding about this issue and why embed interop types will not help in this case.
regards
- Marked As Answer by VS Anywhere Thursday, August 30, 2012 8:57 PM
- Unmarked As Answer by VS Anywhere Thursday, August 30, 2012 8:57 PM
-
Thursday, August 30, 2012 11:51 PM
You're original post was about getting the correct assembly to load, which seems to be a separate issue from mismatched GUIDs. If you reference the Dev11 interop assembly and deploy to a Dev10 machine and do NOT set Embed Interop Types to True, I think you end up encountering your original issue -- the assembly does not even load. A mismatched GUID would typically manifest itself later, when the runtime issues a QueryInterface on the underlying COM implementation. I would expect something like an InvalidCastException or COMException. I would be surprised to see a mismatched interface IID cause a FileNotFound exception.
It would surprise me if a Dev10 interface IID failed a QueryInterface on a Dev11 -- that's all the ComImport attribute is controlling (wrt the GUID). But, if you think the interop assembly really is wrong, you can simply redeclare the interface in your own code, apply the ComImport attribute with the GUID you think is correct and use that. There's nothing "special" about the interface declaration in the interop assembly -- it just tells the runtime what the GUID is for the QueryInterface it ultimately issues to the COM layer. We've successfully used this technique in the past when there was a bug in the definition of the IPersistFileFormat interface. We simply re-declared the interface in our own assembly. Been working great for years.
Hope this helps or that we're at least getting close to being on the same page... ;-)
-
Friday, August 31, 2012 7:33 AM
Hello :)
I really appreciate your help and I completely agree with what you say about COM and GUID and QueryInterface, and I know we can tweak the assemblies and generate our own type information, however this is not something we really want to do for several reasons...
And about you statement:
"it would surprise me if a Dev10 interface IID failed a QueryInterface on a Dev11"
dynamic eng = Activator.CreateInstance(Type.GetTypeFromProgID("VisualStudio.VCProjectEngine.10.0"));
_dteCXXProjectItemEvents = eng.Events as _dispVCProjectEngineEvents_Event;last line (obviously translated to a QueryInterface to object) return null depending on what VS Version you are running it, (do not pay attention to the ProgID, does not matter, you can use either .10 or 11), or depending on what Assembly from Visual Studio you are referencing, probably due the issue that a VS11 COM Object does not provide trough IUnknown the access to previous versions of the object (VS10 COM version), usually should be enough to reference VS11 Dll and redistribute with your app (if this assembly is redistributable), or embed interop types as you propose, however in this case seems more something VS Team people must review than really a configuration/coding problem.
The original post was really a way to notice to Microsoft People what problem will face any VSIX developer that extend the VCProjectEngine and want to have a unique plugin that is able to run both in VS2010 and VS2012, currently using the standard assemblies this is not possible if you do not apply a workaround.
jesus
-
Friday, August 31, 2012 2:13 PMHmmn, wow. Yes I am now confused. Hopefully an MS person can jump in on this one because if it works the way you describe, then it would seem LOTS of folks will have big problems. We certainly wil,l as our product makes heavy use of the VCProjectEngine and the C++ code model and such.
-
Friday, August 31, 2012 2:37 PM
Maybe we are missing something and Microsoft has a good best practise or just point to us what we are doing wrong...
-
Monday, September 03, 2012 2:02 AMModerator
Hi VS,
I will involve some experts into this issue to see whether they can help you out. There might be some time delay, appreciate for your patience.
Thank you for your understanding and support.
Best regards,Ego [MSFT]
MSDN Community Support | Feedback to us
-
Monday, September 03, 2012 1:50 PM
Hello Ego,
Not a problem we have already a workaround for this problem, (using reflection).
We can wait for the right solution from you
Thank you so much
-
Tuesday, September 04, 2012 7:22 PMModerator
Unfortunately, VC++ has always rebuilt it's typelibs and COM objects with different guids from release to release. Consequently, the interop assemblies which are generated for those objects are always specific to a given release.
This has always been a challenge for partners wanting to release an extension targeting multiple versions of VS. For managed code, using reflection is probably the best solution.
Sincerely,
Ed Dore
- Proposed As Answer by Ed DoreMicrosoft Employee, Moderator Tuesday, September 04, 2012 7:22 PM
- Marked As Answer by VS Anywhere Tuesday, September 04, 2012 9:39 PM
-
Tuesday, September 04, 2012 9:42 PM
Hello Ed,
Thank you for this feedback, just now we are sure we are following the best approach to support both VS Versions (2010 and 2012)
regards
jesus
-
Tuesday, September 04, 2012 9:49 PMSo, I'm curious about the details of the reflection-based solution. Seems lots of people should have had this issue for a long time. I know for certain we've used many of the VC++ interfaces for years. Our product was built in VS2005 and targeted VS2008 as well. We never saw any of these issue when moving between those two versions. Is this only for event interfaces or something? I could understand that a bit better since those are IDispatch-based and, thus, you are limited to one interface per COM object. Introducing a new interface necessarily means introducing a new COM coclass. But, for non-IDispatch stuff, I can't see why this would be done and how it wouldn't break LOTs of stuff...
-
Tuesday, September 04, 2012 10:25 PMModerator
As far as I'm aware, this has been a problem for quite some time. I don't know why the VC++ team does this, but it's always done so. If you go back and look at the respective CLSIDs associated with VCProjectEngine, you'll see each has a separate CLSID.
My guess is that the VC++ team couldn't guarantee backward compatibility, and decided to reguid everything on each release.
It's always been a headache for people attempting to target multiple versions of VS (where there extensions utilize the VCProjectEngine object :-(.
With native code, I think most folks just let the C++ compiler build the wrappers for each in a separate namespace, and use the individual classes for a given version of VS. But with managed code (where you need to load a specific interop assembly, reflection is the easiest/best solution I've seen.
I can't recall of PMW packages were native or managed. If you were using something like VCProjectEngine though, and attempting to retrieve (or CoCreate) it, without rebuilding to account for the updated VCProjectEngine.dll, I would expect that to fail if you had simply registered the same package to run under VS2008.
Sincerely,
Ed Dore
-
Wednesday, September 05, 2012 2:06 PM
Hi Ed,
Thanks very much for the detailed explanation. Starting to make more sense.
I'd be interested to see exactly what VS Anywhere did to get around this or what others might have done, as I suspect we're going to run into the exact same thing when we port our package to Dev11. Ours is indeed a managed package.
Thanks in advance.
-
Wednesday, September 05, 2012 11:30 PM
Hello Pacific
Here you have a sample on how to hook an event to the right VCProjectEngine version using reflection depending on what VS version you are running (DTE.Version = "11.0" or DTE.Version = "10.0")
System.Reflection.EventInfo CPPItemAddedEventInfo; Delegate _Instance_CXXItemAddedEventInfo; object _VCEngineEvents = null; dynamic eng; if (VisualStudioVersion == VisualStudio.VisualStudioVersion.Vs2012) eng = Activator.CreateInstance(Type.GetTypeFromProgID("VisualStudio.VCProjectEngine.11.0")); else eng = Activator.CreateInstance(Type.GetTypeFromProgID("VisualStudio.VCProjectEngine.10.0")); // Reflection forever.. _VCEngineEvents = (object)eng.Events; CPPItemAddedEventInfo = eng.Events.GetType().GetEvent("ItemAdded"); _Instance_CXXItemAddedEventInfo = Delegate.CreateDelegate(CPPItemAddedEventInfo.EventHandlerType, this, "_dteCXXProjectItemEvents_ItemAdded"); CPPItemAddedEventInfo.AddEventHandler(eng.Events, _Instance_CXXItemAddedEventInfo);
void _dteCXXProjectItemEvents_ItemAdded(object Item, object ItemParent) { }
Maybe Ed can bring some tips to this implementation,
jesus
- Edited by VS Anywhere Wednesday, September 05, 2012 11:31 PM
- Edited by VS Anywhere Wednesday, September 05, 2012 11:31 PM
- Edited by VS Anywhere Wednesday, September 05, 2012 11:33 PM added event signature
-
Thursday, September 06, 2012 5:25 PMGot it. I'm wondering if something could be done by fielding the AppDomain.AssemblyResolve event? Have you investigated that option at all?
-
Thursday, September 06, 2012 8:19 PM
Hello
Think that will not work because the types for casting (QueryInterface) are already bind to the referenced assembly at compile type by compiler.
So loading one or the other assembly is just the first step, then you need to query for the right interfaces somehow, here is where reflection do the work...
regards
-
Thursday, September 06, 2012 8:54 PM
My suggestion below is theoretical at this point, as I've not tried it. But, it *seems* plausible.
The type names (full names) are baked into your package assembly, but the COMImport attributes are baked into the referenced assembly. So, when your package loads and runs a line of code that references _VCProjectEngineEvents that's going to trigger a load of the referenced assembly. If your package has referenced the Dev10 version of VCProjectEngine and you're running on Dev11, then that load will fail and ultimately land in your implementation of AppDomain.AssemblyResolve. You then resolve it to the Dev11 assembly which brings in the Dev11 types with the Dev11 COMImport attributes and GUIDs.
I didn't think that the *referencing* assembly (your package) had explicit knowledge of any of the COMImport attributes.

