How to create an ActiveX control in C#? - c#

I am not able to create a functioning ActiveX control in C#; I have tried following tutorials to do so without success.
I create a sample Class Library project which includes this code:
namespace AACWCSurvey
{
[ProgId("Prisoner.PrisonerControl")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Class1
{
public Class1()
{
MessageBox.Show("FIRETRUCK!!!");
}
}
}
I then did the following steps:
Properties => Application => Assembly Information => Make Assembly COM-visible
Build => Register for COM interop TRUE (checked)
Make Strong name for assembly (signing)
Build the project
regasm MyDll.dll /tlb /codebase
Can't see Prisoner.PrisonerControl in tstcon32 =(
My OS is WinXP x86.
UPD: it works from VBScript:
Dim objJava
Set objJava = WScript.CreateObject("Prisoner.PrisonerControl")
but it is not visible in tstcon32.

If you read the actual article using the Prisoner.PrisonerControl control a sub key named Control is created inside the key with your control GUID.
On my machine with the guid {9DEA5F06-E324-31A7-837B-D0F3BDE91423} creating the key
HKEY_CLASSES_ROOT\CLSID\{9DEA5F06-E324-31A7-837B-D0F3BDE91423}\Control
Make the control appears in tstcon32. And with or without it the ActiveX is usable for javascript
var x = new ActiveXControl("Prisoner.PrisonerControl");
Actually i had to fight windows on both the javascript execution and registry path to test it on my system because it's an x64 machine but that's another story.

You have created a COM server but not an ActiveX control, which is a far more intricate COM object, the kind that you can exercise with tstcon32.exe.
It must implement a bunch of interfaces, key ones are IOleObject and IOleWindow. The kind of interfaces that allows it to do the required negotiations with an ActiveX host and create a visible window. The Winforms Control class is your best bet to create one.

Here are the relevant steps as documented externally. This is summarized leaving out some exposition but not any necessary steps.
This example is also very similar to the article Using Managed Controls as ActiveX Controls by Garry Trinder, November 25, 2008 and I've included some notes from this article as well.
Exposing Windows Forms Controls as ActiveX controls
This article will describe how to utilise Windows Forms controls
outside of .NET.
Writing the control
Create a new control project from within Visual Studio - my examples are all in C# but VB.NET could also be used.
[Here Garry's article suggests, "First, create a managed usercontrol project – either a Windows Forms class library or control library project. Use the usercontrol designer to design your custom usercontrol the way you want it (using any standard controls you like)."]
Add controls etc to the form, put in the code etc.
Add in the following using clauses...
using System.Runtime.InteropServices;
using System.Text;
using System.Reflection;
using Microsoft.Win32;
Attribute your class so that it gets a ProgID. This isn't strictly necessary as one will be generated, but it's almost always best to be
explicit.
[ProgId("Prisoner.PrisonerControl")]
[ClassInterface(ClassInterfaceType.AutoDual)]
This assigns the ProgID, and also defines that the interface
exposed should be 'AutoDual' - this crufts up a default interface for
you from all public, non-static members of the class. If this isn't
what you want, use one of the other options.
Update the project properties so that your assembly is registered for COM interop.
If you're using VB.NET, you also need a strong named assembly.
Curiously in C# you don't - and it seems to be a feature of the
environment rather than a feature of the compiler or CLR.
Add the following two methods into your class.
[ComRegisterFunction()]
public static void RegisterClass ( string key )
{
// Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it
StringBuilder sb = new StringBuilder ( key ) ;
sb.Replace(#"HKEY_CLASSES_ROOT\","") ;
// Open the CLSID\{guid} key for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(),true);
// And create the 'Control' key - this allows it to show up in
// the ActiveX control container
RegistryKey ctrl = k.CreateSubKey ( "Control" ) ;
ctrl.Close ( ) ;
// Next create the CodeBase entry - needed if not string named and GACced.
RegistryKey inprocServer32 = k.OpenSubKey ( "InprocServer32" , true ) ;
inprocServer32.SetValue ( "CodeBase" , Assembly.GetExecutingAssembly().CodeBase ) ;
inprocServer32.Close ( ) ;
// Finally close the main key
k.Close ( ) ;
}
The RegisterClass function is attributed with ComRegisterFunction -
this static method will be called when the assembly is registered for
COM Interop. All I do here is add the 'Control' keyword to the
registry, plus add in the CodeBase entry.
CodeBase is interesting - not only for .NET controls. It defines a URL
path to where the code can be found, which could be an assembly on
disk as in this instance, or a remote assembly on a web server
somewhere. When the runtime attempts to create the control, it will
probe this URL and download the control as necessary. This is very
useful when testing .NET components, as the usual caveat of residing
in the same directory (etc) as the .EXE does not apply.
[ComUnregisterFunction()]
public static void UnregisterClass ( string key )
{
StringBuilder sb = new StringBuilder ( key ) ;
sb.Replace(#"HKEY_CLASSES_ROOT\","") ;
// Open HKCR\CLSID\{guid} for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(),true);
// Delete the 'Control' key, but don't throw an exception if it does not exist
k.DeleteSubKey ( "Control" , false ) ;
// Next open up InprocServer32
RegistryKey inprocServer32 = k.OpenSubKey ( "InprocServer32" , true ) ;
// And delete the CodeBase key, again not throwing if missing
k.DeleteSubKey ( "CodeBase" , false ) ;
// Finally close the main key
k.Close ( ) ;
}
The second function will remove the registry entries added when (if)
the class is unregistered - it's always a good suggestion to tidy up
as you go.
Now you are ready to compile & test your control.
Additional notes from Garry's blog:
[The] additional registry entries: Control, MiscStatus, TypeLib and
Version [can be created] with a .REG script, but it’s generally better
to write functions that will be called on registration/unregistration
He describes the registry keys in some detail:
Control is an empty subkey. TypeLib is mapped to the GUID of the
TypeLib (this is the assembly-level GUID in the assemblyinfo.cs).
Version is the major and minor version numbers from the assembly
version. The only mildly interesting subkey is MiscStatus. This needs
to be set to a value composed of the (bitwise) values in the OLEMISC
enumeration, documented here. To make this enum available, add a
reference to Microsoft.VisualStudio.OLE.Interop (and a suitable
‘using’ statement for the namespace).
His final note is a warning:
Note: this seems to work OK for Excel (with the very limited testing
I've done), partly works with PowerPoint, but fails miserably with
Word. Possibly, some more of the OLEMISC values might improve this;
possibly there are some messages we need to hook; possibly there are
some more interfaces we need to implement ... The fact that I’ve only
barely got it to work in a very limited way should tell you that this
is probably not a technique you want to use in any serious way.

Related

C# use same DLL (assembly) with and without "extern alias"

I am basically doing the same work as described in extern alias with same assembly file name: I am writing a converter for classes between different versions of a software, so that the XML settings of these classes can be converted.
The conversion is ready and working ("extern alias" are set where needed).
Now I want to change my small test project into a full program. The conversion uses the DLLs of V22, V23 and V24, each with respective alias, and the program should use the latest version of the DLLs (currently V24, without alias, thus global) for its own operation. The problem is, that the program does not find any types from the referenced global DLLs.
Is Visual Studio (I'm using v.2015 U3) maybe not able to distinguish between the DLLs if the same DLL is used with and without alias?
The project references:
basics, path=..\v24.., no alias (#1)
basics v=22, path=..\v22.., alias=V22
basics v=23, path=..\v23.., alias=V23
basics v=24, path=..\v24.., alias=V24 (#1)
imaging v=22, path=..\v22.., alias=V22
imaging v=23, path=..\v23.., alias=V23
imaging v=24, path=..\v24.., alias=V24
...
I supspect that the marked (#1) assemblies collide somehow.
Is this correct?
Any solution or workaround?
I could add "extern alias V24" in every file of the general part of the program, but then I'd have to change that to "extern alias V25" when the next version of the DLLs is released. I'd like to avoid that extra work.
I found an acceptable workaround.
Instead of adding the alias to every "using" when I switch to another version, e.g. replace "/* v24 */" by "swcore_v_0_22"
using CImageObject_V_0_22 = swcore_v_0_22.SwCore.CImageObject;
using CImageObject_V_0_24 = /* v24 */ SwCore.CImageObject;
using CImageObjectStandard_V_0_22 = swcore_v_0_22.SwCore.CImageObjectStandard;
using CImageObjectStandard_V_0_24 = /* v24 */ SwCore.CImageObjectStandard;
using CImageObjectCombi_V_0_22 = swcore_v_0_22.SwCore.CImageObjectCombination;
using CImageObjectCombi_V_0_24 = /* v24 */ SwCore.CImageObjectCombination;
I can simply add a nested using, which is not possible by default but possible when placed inside a different namespace, see https://stackoverflow.com/a/35921944/2505186. The namespace exists anyway.
using SwCore_v_0_22 = swcore_v_0_22.SwCore;
using SwCore_v_0_24 = global::SwCore;
namespace ConfigEditor
{
using CImageObject_V_0_22 /**/= SwCore_v_0_22.CImageObject;
using CImageObject_V_0_24 /**/= SwCore_v_0_24.CImageObject;
using CImageObjectStandard_V_0_22 /**/= SwCore_v_0_22.CImageObjectStandard;
using CImageObjectStandard_V_0_24 /**/= SwCore_v_0_24.CImageObjectStandard;
using CImageObjectCombi_V_0_22 /**/= SwCore_v_0_22.CImageObjectCombination;
using CImageObjectCombi_V_0_24 /**/= SwCore_v_0_24.CImageObjectCombination;
Later I will replace "global" by "swcore_v_0_24" in all files, which still is some work, but much less than before. And since it is replacing instead of adding, it can do it automatically.
I could theoretically also replace "/* v24 */", but that could break the nice vertical alignment depencing of the length of the replacement. ;-)

Accessing CommandBarButton.Picture property the late bound way

I try to read the information of Microsoft Access (Office) CommandBars contained in an *.mdb. I could use Microsoft.Office.Interop.Access for this; however, these PIA assemblies are tied to specific Office versions. Therefore, to be version independent, I do it the late-bound way through C#'s dynamic type. I.e., I have no references to Microsoft Office specific assemblies. The price for this is that the access to the command bars is now weakly typed.
This approach works well, except when I try to access the custom button images. This is a condensed version of my real code to illustrate the problem:
dynamic access = Activator.CreateInstance(Type.GetTypeFromProgID("Access.Application", true));
// Starts an Access XP (2002) process in my case, but could be any version.
foreach (dynamic commandBar in access.CommandBars) {
if (!commandBar.BuiltIn) { // Only my menus and toolbars.
foreach (dynamic control in commandBar.Controls) {
if (control.Type == (int)MsoControlType.msoControlButton && !control.BuiltInFace) {
string caption = control.Caption; // Works.
stdole.IPictureDisp picture = control.Picture; // <==== Throws exception! ====
// ...
}
}
}
}
Calling the getter of the CommandBarButton.Picture property throws this exception:
Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED))
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Dynamic.ComRuntimeHelpers.CheckThrowException(Int32 hresult, ExcepInfo& excepInfo, UInt32 argErr, String message)
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at MyApplication.MyMethod in MyApplication.cs:line 64
How can I get the picture avoiding this exception?
Note, this question is not about converting the IPictureDisp object to a System.Drawing.Image object. I already have a solution for this. Also, it makes no difference whether picture is typed as object, dynamic, or IPictureDisp. The property does exist, otherwise I would get the exception 'System.__ComObject' does not contain a definition for 'Picture'.
It is a .NET Framework 4.0 Windows Forms project compiled for x86 (32-bit).
Edit: Switching to Primary Interop Assemblies (PIA) allows strong typing, but does not solve the problem. The exception persists.
Meanwhile I have read more about PIAs. Since .NET Framework 4.0 you can tweak the properties of the assembly reference to make them version independent. Right click on References / Microsoft.Office.Interop.Access and
Set Embed Interop Types to True.
Set Specific Version to False.
Do this for References / office as well. Therefore I will be switching to PIAs instead of using dynamic.

The component does not have a resource identified by the uri

I want to create a Generic DataGrid to use on all my Views/UserControls.
This is my structure:
Class Library called "Core":
Class called "ViewBase":
public class ViewBase : UserControl
{
public ViewBase()
{
}
//Rest of Methods and Properties
}
Class Library called "Controls":
UserControl Called "GridView":
XAML:
<vb:ViewBase x:Class="Controls.GridView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vb="clr-namespace:Core;assembly=Core">
<Grid>
<DataGrid></DataGrid>
</Grid>
</vb:ViewBase>
Code Behind:
using Core;
public partial class GridView : ViewBase
{
public GridView ()
{
InitializeComponent();
}
}
Then is the WPF Aplication called "WPFApp":
Class called "View":
using Controls;
public class View : GridView
{
public View()
{
InitializeComponent();
}
}
My whole idea is to use GridView where i need a DataGrid.
When i run the application i get this error:
"The component 'WpfApp.View' does not have a resource identified by the URI '/Controls;component/GridView.xaml'."
What am i doing wrong?
Is this the correct approach or am i way off?
Frustratingly, I had exactly this error and spent forever trying to work out the cause. For me, it was once working but then I made some very minor changes to the XAML of the derived control, and the compiler started giving that error message.
Short solution, cutting out many hours of trying to figure it out: shut down Visual Studio and re-opened it, recompiled, problem magically went away! (This is VS2012 Pro)
Just added this in case anyone reading is going round in circles trying to find a non-existent problem with their code. Might be worth trying the "IT Crowd solution" first.
This gave me headaches for 3 days! I have a XAML UserControl in a class library and a class (only C#) that derives from the UserControl in my .exe project.
In xaml designer of my MainWindow.xaml and when starting the application, I got the error "component does not have a resource identified by the uri".
The answer of "Juan Carlos Girón" finally lead me to the solution:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using System.Reflection;
using System.IO.Packaging;
using System.Windows.Markup;
namespace ClassLibrary1
{
static class Extension
{
public static void LoadViewFromUri(this UserControl userControl, string baseUri)
{
try
{
var resourceLocater = new Uri(baseUri, UriKind.Relative);
var exprCa = (PackagePart)typeof(Application).GetMethod("GetResourceOrContentPart", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { resourceLocater });
var stream = exprCa.GetStream();
var uri = new Uri((Uri)typeof(BaseUriHelper).GetProperty("PackAppBaseUri", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null, null), resourceLocater);
var parserContext = new ParserContext
{
BaseUri = uri
};
typeof(XamlReader).GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { stream, parserContext, userControl, true });
}
catch (Exception)
{
//log
}
}
}
}
and called that from by UserControl's .cs file:
namespace ClassLibrary1
{
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
//InitializeComponent();
this.LoadViewFromUri("/ClassLibrary1;component/myusercontrol.xaml");
}
}
}
Thanks again to "Juan Carlos Girón"!
The reason you are getting this error is because the way InitializeComponent that is implemented (in VS 2010) will always search in the derived class's assembly.
Here is InitializeComponent:
/// <summary>
/// InitializeComponent
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
public void InitializeComponent() {
if (_contentLoaded) {
return;
}
_contentLoaded = true;
System.Uri resourceLocater = new System.Uri("/WpfApplication1;component/mainwindow.xaml", System.UriKind.Relative);
#line 1 "..\..\..\MainWindow.xaml"
System.Windows.Application.LoadComponent(this, resourceLocater);
#line default
#line hidden
}
The line where it looks up your XAML resource is System.Windows.Application.LoadComponent(this, resourceLocator). And this most probably fails because equivalent of 'this.GetType().Assembly' is used to determine which assembly to search for the resource identified by the relative Uri. And 'this.GetType()' does get the derived type of the object, not the type of the class where the code is implemented.
PS. Is this a bug? I do not know...
You can try this approach
I created my own InitializeComponent() and I called this way
this.LoadViewFromUri("/NameOfProject;component/mainwindow.xaml");
public static void LoadViewFromUri(this Window window, string baseUri)
{
try
{
var resourceLocater = new Uri(baseUri, UriKind.Relative);
var exprCa = (PackagePart)typeof(Application).GetMethod("GetResourceOrContentPart", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { resourceLocater });
var stream = exprCa.GetStream();
var uri = new Uri((Uri)typeof(BaseUriHelper).GetProperty("PackAppBaseUri", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null, null), resourceLocater);
var parserContext = new ParserContext
{
BaseUri = uri
};
typeof(XamlReader).GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { stream, parserContext, window, true });
}
catch (Exception)
{
//log
}
}
I was doing something very similar with the same result. I had one C# class library that contained a WPF control called UsageControl (xaml with accompanying xaml.cs file). In a separate C# project(i.e. separate dll) I created a C# class CPUUsageControl which inherited from UsageControl, but put its own spin on it. When I tried to use the CpuUsageControl on one of my views I got the same error you did.
What I did to fix that was in my seperate assembly, instead of creating a class that inherited from the base control, i created a new WPF Control that contained the base control. I then put all of the logic that was contained in the CpuUsage class into the WpfCpuUsageControl's code behind. I was able to use this object is all of my other controls just fine.
For your Control "GridView" i would create a new WPF user control, call it GridView and make it contain a "ViewBase" as the content of the Grid control.Inside of the ViewBase's content put in your DataGrid, like this:
<UserControl....>
<Grid>
<ViewBase name="vBase">
<DataGrid name="dGrid" />
</ViewBase>
</Grid>
</UserControl>
It is also not apparent to me that you need ViewBase to inherit from UserControl directly. If all you want are for your controls to have certain properties and method why not just make a BaseControl class (that does not inherit from anyone but object) and have future controls inherit from it. Perhaps an abstract base class or interface is what you're after.
For MVVM WPF projects, I typically have a BaseViewModel which implements INotifyPropertyChanged for me so I don't have to do that same code everywhere.
Best of luck, I know this problem was a huge pain to figure out. The exception message and google are most unhelpful!
Same problem here.
Short version:
Copy Local has to be set to False!
Long version:
We developed a WPF solution (MVVM, 20 projects) and implemented a plug-in system. Our /bin/Debug directory contains the executable, some dll files and a plugin directory that contains the plugins.
There is one project "DialogLib" (Class library, kind of dialog) that defines a window (the view), the ViewModel, Model and some interfaces. One of the plugins used one of the interfaces of DialogLib. The window itself is opened by the main application.
To use the interface of the 'DialogLib' library in the plugin we had to add a project reference of DialogLib to the plugins project references. When the application was started, the plugins were loaded. If the user then selects a menu item, the window should open. At this point the error "... component does not have a resource identified by the URI ..." occured when the windows code behind tried to execute its InitializeComponent().
Where's the problem?
The problem is, that, when we built the solution VS has created the DialogLib.dll correctly and copied it to /bin/Debug/. This is because the main application file wants to open the window. But DialogLib.dll was also copied to /bin/Debug/plugins because one of the plugins referenced it to use one of the interfaces defined in DialogLib.dll. So what?
When the plugin is loaded at runtime it uses the interface defined in /bin/Debug/plugins/DialogLib.dll. and the main application file tries to open the window defined in /bin/Debug/DialogLib.dll. Although the files are identical, VS runs into trouble. Setting the value of Copy Local of the DialogLib reference properties of the plugins references avoids copying DialogLib.dll to /bin/Debug/plugins and thus solves the problem.
We had a similar same problem (but different error) in another project where we wanted to use a type TypeA, that was defined in a dll file, in a plugin and in the main application. Copy Local was set to true which caused a copy of the dll file to be located in ../bin/Debug/plugins and in ../bin/Debug/. It turned out that, even though it was the same dll file, the TypeA in the main app file and TypeA in the plugin were treated as different types respectively as types which could not be exchanged.
Delete obj folder
Delete bin folder
Rebuild solution
Worked for me!
Also if you are loading assemblies using Assembly.LoadFile, check out AppDomain.CurrentDomain.GetAssemblies() for duplicate assemblies in the current AppDomain. Because in auto-generated code of WPF UserControl, the component will be loaded using its relative URI. And since there are duplicate assemblies in the current AppDomain, application doesn't know which one to use.
I resolved this by placing
myusercontrol = Activator.CreateInstance<myusercontrol>();
in the constructor of the window containing the usercontrol before the InitializeComponent(); line
I received the same error when using Visual Studio 2013.
The component does not have a resource identified by the uri
Tried:
Cleaning and rebuilding the solution - did not work.
Closing and opening Visual Studio - did not work.
Solution:
Went into the projects bin directory and cleared out all files.
Ran the project again and worked fine.
Open the Package Manager Console which will open in the root directory of your Solution and run the following powershell command:
Get-ChildItem -inc bin,obj -recurse | Remove-Item -recurse -force -EA SilentlyContinue
#Willem, this seems perfectly OK to me. In fact I tried this and it worked in my case. I used ListBox instead of DataGrid (but that shouldnt matter).
All my namespaces were in one assembly. So I used a common parent namespace for all e.g.
MyWpfApplication.Controls
MyWpfApplciation.GridView
MyWpfApplciation.ViewBase
Coz all these Controls, GridView, ViewBase are clashing with existing System or System.Windows.Controls based namespace and class declarations. So I made sure I referred correct ones MyWpfApplication.* in my project.
I just ran into this problem as well without any inheritance issues. I was just referencing a DLL that contained a dialog and trying to create and display that dialog.
I have assembly resolver that loads assemblies from a specific folder and it turns out that I had added the reference in VS and had not turned off Copy Local. Long story short: my process had loaded two versions of that same DLL. This seems to confuse WPF (or the runtime). Once I cleared the Copy Local and deleted the extra DLL copies, it worked fine again.
I got this error after renaming a xaml file. Reversing the renaming solved the problem.
Furthermore, I found that a reference to the xaml file name in App.xaml was not updated (the StartupUri), but renaming that to the current name didn't resolve the problem (but maybe it does for you). Basically, I can't rename the xaml file.
Fyi, for me, the component 'complaining' in the error was SplitComboBox.
Happend to me when I had the same project opened in two solutions. Modifying the base-control in one project cause the other project to have this problem. If closing and opening doesn't work, then delete all the folders in "C:\Users...\AppData\Local\Microsoft\VisualStudio\12.0\Designer\ShadowCache"
This can happen also when closing and reopening a window. So it could also have nothing to do with packages and/or dlls.
I solved the problem thanks to the solution posted by PainElemental, which is IMHO underrated:
namespace MyNamespace
{
public partial class MyDialog : Window
{
public MyDialog(ExcelReference sheetReference)
{
this.LoadViewFromUri("/MyApp;component/mynamespace/mydialog.xaml");
}
}
}
LoadViewFromUri is implemented as an extension, as PainElemental wrote.
The craziest is that I also wrote in the same project other windows without encountering any problem.
Thank you PainElemental, you ended my protracted pain!
I started consistently seeing a "the component does not have a resource identified by the uri" error when I clicked a particular menu choice from an installed product that was working on other computers. I tried uninstalling the product, making sure its files really were gone, rebooting, and reinstalling the product. The problem remained. I deleted the contents of my %TEMP% directory, and the problem ceased.
Thanks for all the tips in this thread. I think my own variation of this error was for a slightly different reason again, so I'll post here in case it's of use.
In my situation, the error occurred when invoking window.ShowDialog(). More specifically, my window is defined in a separate class library assembly (let's call it AssemblyA.dll).
I have multiple versions of AssemblyA which are used in various products, some of which are plugins and some aren't. In short, the consequence is that the process might end up loading several different strong-named versions of AssemblyA. So there are duplicate assemblies in the app domain as #VahidN pointed out, but they're strictly differently versioned assemblies which are meant to be there, and merely share the same AssemblyShortName.
WPF's auto-generated code for InitializeComponent() looks like this:
public void InitializeComponent() {
if (_contentLoaded) {
return;
}
_contentLoaded = true;
System.Uri resourceLocater = new System.Uri("/AssemblyA;component/forms/mywindow.xaml", System.UriKind.Relative);
#line 1 "..\..\..\Forms\MyWindow.xaml"
System.Windows.Application.LoadComponent(this, resourceLocater);
#line default
#line hidden
}
It's only referring to the short name of AssemblyA, and not to the specific version or public key token of AssemblyA in which the InitializeComponent() method is running. The consequence is that the code just seems to find the first AssemblyA assembly loaded into the process, searches for the XAML, can't find it (because it's found an older version of the assembly first), and then throws an exception. Or perhaps it finds something but maybe it's pulled a different XAML resource than what it's meant to have, from either an older or newer version of the assembly that happens to also be loaded.
It's not perfect, but I've consulted the Pack URI specification, and worked around this by writing my own extension method that makes sure the assembly is found with the appropriate version and public key token, rather than simply the AssemblyShortName.
In case it's of use for others, here's a simplified version of what I've ended up with.
public static void AssemblySensitive_InitializeComponent(this ContentControl contentControl, string componentString)
{
// Strictly speaking this check from the generated code should also be
// implemented, but it doesn't fit directly into an extension method.
//if (_contentLoaded)
//{
// return;
//}
//_contentLoaded = true;
var asm = System.Reflection.Assembly.GetExecutingAssembly();
var shortName = asm.GetName().Name;
var publicKeyToken = GetPublicKeyTokenFromAssembly(asm);
var version = asm.GetName().Version.ToString();
System.Uri resourceLocater = new System.Uri($"/{shortName};V{version};{publicKeyToken};{componentString}", System.UriKind.Relative);
System.Windows.Application.LoadComponent(contentControl, resourceLocater);
}
/// <summary>
/// Gets a public key token from a provided assembly, and returns it as a string.
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
/// <remarks>Adapted from https://stackoverflow.com/questions/3045033/getting-the-publickeytoken-of-net-assemblies</remarks>
private static string GetPublicKeyTokenFromAssembly(System.Reflection.Assembly assembly)
{
var bytes = assembly.GetName().GetPublicKeyToken();
if (bytes == null || bytes.Length == 0)
return "None";
var publicKeyToken = string.Empty;
for (int i = 0; i < bytes.GetLength(0); i++)
publicKeyToken += string.Format("{0:x2}", bytes[i]);
return publicKeyToken;
}
The _contentLoaded bit could probably be done with extension properties, but I need the code for this library to compile in C# 7.3 so I have a much longer workaround which I removed so as not to distract.
Then I call it from the constructor like this:
public MyWindow()
{
// Don't use the auto-generated initialize, because if multiple different versions
// are loaded into the process, it can try to load the resource from the wrong one.
//InitializeComponent();
AssemblySensitive_InitializeComponent("component/forms/mywindow.xaml");
// ... do more constructor stuff ...
}
I spent ages getting frustrated trying to figure out what was going on, so I hope this helps someone else out there.
As others have pointed out in their answers, this will happen if you have a base control class with an associated XAML resource, and then define a class in a separate assembly that inherits from the base control. This happens because of a limitation in WPF.
WPF is open source now, so you can see the source code that we need to work around that is called in IntializeComponent() (though it's a bit difficult to follow). In summary, this method get a stream for the control's XAML resource and then loads it with XamlReader.LoadBaml(). The issue is that the framework code does not load the XAML resource file correctly when the derived class is in a different assembly than the XAML resource file.
To work around this issue we need to load the XAML resource stream correctly and then call XamlReader.LoadBaml() manually. There are a few other answers here already that do exactly this, but here's my take on it. The following extension method is a bit more concise than the other answers, accesses only one private method via reflection, and also guards against multiple calls.
private static MethodInfo? _loadBamlMethod;
public static void InitializeComponent(this ContentControl control, string xamlResourceUri, ref bool contentLoaded)
{
// Ensure the control is only initialized once
if (contentLoaded) return;
contentLoaded = true;
// Use reflection to get the private XamlReader.LoadBaml() method and cache the result
_loadBamlMethod ??= typeof(XamlReader).GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static)
?? throw new InvalidOperationException("Could not find XamlReader.LoadBaml() via reflection");
// Load the XAML resource for the control
var stream = Application.GetResourceStream(new Uri(xamlResourceUri, UriKind.Relative)).Stream;
var parserContext = new ParserContext { BaseUri = PackUriHelper.Create(new Uri("application://")) };
_loadBamlMethod.Invoke(null, new object[] { stream, parserContext, control, true });
}
Which can then be used like this. Controls in other assemblies may now inherit from BaseControl and not see this issue.
public partial class BaseControl : UserControl
{
protected BaseControl()
{
// The resource URI here can be coped from the generated partial class
// Note that we are also re-using the _contentLoaded field defined in the generated partial class
this.InitializeComponent("/Senti.Common.PrismModules.Hmi;component/controls/basecontrol.xaml", ref _contentLoaded);
}
}
It should definitely be noted that this workaround (as well as the ones in other answers) work by accessing a private method within the WPF framework, which is obviously not a supported use case. That said, I have developed and tested this approach with the .NET 5 version of WPF and not seen any issues. Microsoft has also said that very little development is planned for the WPF framework other than bugfixes etc, so this workaround should be fairly stable.
Quicker than closing all of Visual Studio is just to kill XDescProc.exe in your task manager.
XDescProc is the designer. The moment the process is closed you'll see a Reload the designer link in visual studio. Click that and XDes will be started again and your 'no resource' error should be gone.
Here's the link visual studio shows after you kill the designer process:
I had accidently deleted a user control via a rename/copy action. When I reinstated the project file and the xaml file and .cs from version control this error started happening in the design studio for that control which had mistakenly been deleted/renamed.
That suggested some type of cache on the file in question....so closing Visual Studio, deleting the bin directory and rebuilding worked.
Followed PainElemental's solution (to clarify, for his code the ClassLibrary1 for me was the .dll name without the .dll extension), here's my scenario in case it helps anyone link their specific error messages to the problem:
I use dll's to load and run usercontrols into a main program as their own popup windows. PainElemental's solution was mostly working , but 1 of the 3 classes in my "popup .dll" wouldn't load properly. I would get an exception with 2 inner exceptions, like:
mscorlib InvokeMethod...;
WpfXamlLoader.Load...Provide value on...StaticResourceExtension...;
ResolveBamlType....method or operation is not implemented.
In my case, I confirmed it would load the new URI and work in testing, but when I tried to run it over in my Live environment it would error in LoadViewFromUri().
As I tested further, I narrowed down the issue to not being able to load a separate "library .dll" file I was using which contained a Converter I was using in the .xaml file of the class which was failing, and on further research the issue there was that the Live environment was using a different "library .dll" version than I was using in my test environment, even though the exception message from my "popup .dll" did not make any mention of that.
For reference, I use Copy Local=True and that didn't give me issues. To best debug these kinds of issues, an understanding of the locations where .dll files are searched for by the .exe is helpful. As I understand it, when you are running projects in VS, when Copy Local=True the .dlls get copied to the same folder as the .exe when it is Built. When the .exe is run the standard location it will search for .dlls is the same folder as the .exe. Additional locations that the .exe can look for .dlls can be set in the .exe.config file, in the probing element. In the below example, it can also search in a 'MyDLLs' and the 'MyDLLs\Core' directory relative to the .exe's location. Note that it will not naturally search any subfolders, you have to specify them explicitly. I believe it also searches the GAC, but I currently have minimal knowledge concerning GAC.
<configuration>
...
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="MyDLLs;MyDLLs\Core;"/>
</assemblyBinding>
</runtime>
</configuration>
Hi the way solve this problem was to rename the xaml usercontrol to all smallcaps on InitializeComponent()...
enter image description here
enter image description here
For me, when trying to launch a window dialog (window.ShowDialog()) in my application during startup, the exception was thrown in the InitializeComponent method in the window's class constructor.
After much head scratching I had discovered that the issue was that an app.publish folder was getting created in the debug directory, which contained the application exe only. Deleting the app.publish folder resolved this exception. See the following article to prevent this folder from getting created:
What creates the directory "app.publish" in visual studio 2013?

How to copy data to clipboard in C#

How can I copy a string (e.g "hello") to the System Clipboard in C#, so next time I press CTRL+V I'll get "hello"?
There are two classes that lives in different assemblies and different namespaces.
WinForms: use following namespace declaration, make sure Main is marked with [STAThread] attribute:
using System.Windows.Forms;
WPF: use following namespace declaration
using System.Windows;
console: add reference to System.Windows.Forms, use following namespace declaration, make sure Main is marked with [STAThread] attribute. Step-by-step guide in another answer
using System.Windows.Forms;
To copy an exact string (literal in this case):
Clipboard.SetText("Hello, clipboard");
To copy the contents of a textbox either use TextBox.Copy() or get text first and then set clipboard value:
Clipboard.SetText(txtClipboard.Text);
See here for an example.
Or... Official MSDN documentation or Here for WPF.
Remarks:
Clipboard is desktop UI concept, trying to set it in server side code like ASP.Net will only set value on the server and has no impact on what user can see in they browser. While linked answer lets one to run Clipboard access code server side with SetApartmentState it is unlikely what you want to achieve.
If after following information in this question code still gets an exception see "Current thread must be set to single thread apartment (STA)" error in copy string to clipboard
This question/answer covers regular .NET, for .NET Core see - .Net Core - copy to clipboard?
For console projects in a step-by-step fashion, you'll have to first add the System.Windows.Forms reference. The following steps work in Visual Studio Community 2013 with .NET 4.5:
In Solution Explorer, expand your console project.
Right-click References, then click Add Reference...
In the Assemblies group, under Framework, select System.Windows.Forms.
Click OK.
Then, add the following using statement in with the others at the top of your code:
using System.Windows.Forms;
Then, add either of the following Clipboard.SetText statements to your code:
Clipboard.SetText("hello");
// OR
Clipboard.SetText(helloString);
And lastly, add STAThreadAttribute to your Main method as follows, to avoid a System.Threading.ThreadStateException:
[STAThreadAttribute]
static void Main(string[] args)
{
// ...
}
My Experience with this issue using WPF C# coping to clipboard and System.Threading.ThreadStateException is here with my code that worked correctly with all browsers:
Thread thread = new Thread(() => Clipboard.SetText("String to be copied to clipboard"));
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start();
thread.Join();
credits to this post here
But this works only on localhost, so don't try this on a server, as it's not going to work.
On server-side, I did it by using zeroclipboard. The only way, after a lot of research.
Clipboard.SetText("hello");
You'll need to use the System.Windows.Forms or System.Windows namespaces for that.
Clip.exe is an executable in Windows to set the clipboard. Note that this does not work for other operating systems other than Windows, which still sucks.
/// <summary>
/// Sets clipboard to value.
/// </summary>
/// <param name="value">String to set the clipboard to.</param>
public static void SetClipboard(string value)
{
if (value == null)
throw new ArgumentNullException("Attempt to set clipboard with null");
Process clipboardExecutable = new Process();
clipboardExecutable.StartInfo = new ProcessStartInfo // Creates the process
{
RedirectStandardInput = true,
FileName = #"clip",
};
clipboardExecutable.Start();
clipboardExecutable.StandardInput.Write(value); // CLIP uses STDIN as input.
// When we are done writing all the string, close it so clip doesn't wait and get stuck
clipboardExecutable.StandardInput.Close();
return;
}
If you don't want to set the thread as STAThread, use Clipboard.SetDataObject(object sthhere):
Clipboard.SetDataObject("Yay! No more STA thread!");
This works on .net core, no need to reference System.Windows.Forms
using Windows.ApplicationModel.DataTransfer;
DataPackage package = new DataPackage();
package.SetText("text to copy");
Clipboard.SetContent(package);
It works cross-platform. On windows, you can press windows + V to view your clipboard history
On ASP.net web forms use in the #page AspCompat="true", add the system.windows.forms to you project.
At your web.config add:
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="false" />
</appSettings>
Then you can use:
Clipboard.SetText(CreateDescription());
If you don't want too or cannot use System.Windows.Forms you can use the Windows native api: user32 and the clipboard functions GetClipboardData and SetClipboardDat (pinvoke)
A .NET 6 wrapper library can be found here https://github.com/MrM40/WitWinClipboard/tree/main

warning MSB3391: <DLL> does not contain any types that can be unregistered for COM Interop

I've made a simple C# DLL (that's part of a much larger project) using VS2005. I need to use the DLL in Excel via VBA code so I am using COM Interop on the assembly. I am trying to make the build process automatically generate the necessary TLB file so that I don't need to go to the command line and use regasm after every build.
My problem is that although the DLL compiles and builds fine, it does not generate a TLB file. Instead, the error in the title prints out in the output box.
I've gotten other DLLs to build TLB files by going to the project's properties in VS2005 -> Build -> Output -> Check "Register for COM interop". Also I have [assembly: ComVisible(true)] in the AssemblyInfo.cs.
Here's the summary of the source for the problem DLL and the DLL that it references for a return type:
using System;
using System.IO;
using System.Runtime.InteropServices;
using SymbolTable;
namespace ProblemLibrary
{
public class Foo
{
public Foo(string filename)
{
...
}
// method to read a text file into a SymbolTable
public SymbolTable BuildDataSet(string[] selected)
{
...
}
}
}
Here is a summary of SymbolTable.dll. It holds a return type that ProblemLibrary uses.
using System;
using System.Collections.Generic;
namespace SymbolTable
{
public class SymbolTable
{
readonly Dictionary<SymbolInfoStub, string> _symbols = new Dictionary<SymbolInfoStub, string>();
/*methods that interact with Dictionary snipped*/
}
}
You need to have ctor without any params.
You should have GuidAttribute and ProgIdAttribute around the classes.
Its better to mark the assembly as ComVisible(false) and mark explicitly the classes that need export.
Use interfaces for your classes.
Make sure the you have GuidAttribute in the assembly level.
[Guid("<PUT-GUID-HERE-1>")]
[ComVisible(true)]
interface IFoo
{
void DoFoo();
}
[Guid("<PUT-GUID-HERE-2>")]
[ComVisible(true)]
[ProgId("ProgId.Foo")]
class Foo : IFoo
{
public void DoFoo()
{
}
}
In the AssemblyInfo.cs file, make sure you have the following:
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]
UPDATE:
Read: How can I make use of .NET objects from within Excel VBA?
Which links to:
http://richnewman.wordpress.com/2007/04/15/a-beginner%E2%80%99s-guide-to-calling-a-net-library-from-excel/
I saw a similar problem. I got an error like:
warning MSB3391: does not contain any
types that can be unregistered for COM
Interop.
I followed all the rules (ComVisible, etc.) but nothing worked.
Solution: I had to put something in the default constructor so that it would not be optimized away. The moment I had something there, the registration finished with no message and the component was visible in the registry.
Interesting note: a friend of mine managed to register the original DLL with the empty default constructor on his machine (64-bit Windows-7, VS2008-Professional, like mine). However, his REGASM.EXE was:
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\regasm.exe
while mine was:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe
So it could be some difference between versions of the .NET framework - maybe the later version is optimizing too much and the REGASM does not account for that.

Categories

Resources