Add-In Component not found in Assembly | SCVMM Add-In SDK - c#

I am programing an add-in for the scvmm and I have no Idea why this is happening:
This is my uncompiled add-in.dll:
[AddIn("Backup HyperV VM")]
public class BackupHyperVVM : ActionAddInBase
{
public override bool CheckIfEnabledFor(IList<ContextObject> contextObjects)
{
if (contextObjects != null && contextObjects.Count > 0)
return true;
return false;
}
public override void PerformAction(IList<ContextObject> contextObjects)
{
}
private void execPSS(string param) //Execute a powershell script within the SCVMM -- need to make shure I run it on the right host
{
PowerShellContext.ExecuteScript<ServerConnection>(param,
(items, error) =>
{
//code to set server info here
if (error == null)
{
//on Success
}
else
{
//on Error
}
});
}
}
And this is the manifest.xml:
<ConsoleAddIn
xmlns="urn:VMM-AddIns-v1-CTP"
Name="VMM Backup Add-In"
Version="0.0.1.0"
Author="..."
Description="This Add-In (once finished) provides the user with a GUI solution to backup and restore VMs from a Hyper-V host."
FolderName="BackupAddIn"
TrustLevel="Full">
<ActionAddIn
Name="Backup VMs Add-In"
Contexts="Cluster"
AssemblyName="add-in.dll"
ShowInContextMenu = "True"
ActionType="Code"
Icon="Ico.ico">
<ButtonLabel>
Backup VM
</ButtonLabel>
</ActionAddIn>
</ConsoleAddIn>
When I zip the files and try to load the add-in, I get this error (I translated it from German):
The Add-In-Component "Backup VMs Add-In" can't be found in the Assembly "add-in". Possible reasons:
1. The Attribute "Name" of the Add-In is not matching the Name defined in the Attribute "AddIn" in the Add-In-Class.
2. The Add-In-Class is not public.
Thank you for your help..I have no idea how to solve this, even the docs couldn't help me.

From what I can see, you need to change the manifest entry from AssemblyName="add-in.dll" to AssemblyName="BackupHyperVVM".

Related

How can we call Invoke(Delegate) method from a Microsoft Office Add-in?

Question: How can we use Invoke(...) method in a Microsoft Office 2010-2016 VSTO project. Or, are there any work around, alternatives etc for a VSTO project?
Background: I am trying to implement a source code of a third party Windows Forms application into my Microsoft Office VSTO add-in project. The third party vendor has provided its users a sample/example as a Windows Form project with source code and has asked its users to mimic that code to their other projects (WPF, Office Solutions etc.).
I have been able to implement all parts of their Windows Forms sample app except the following line of code that is not recognized by the VS2019 in my VSTO project:
Invoke(new IsActivatedDelegate(CheckIfActivated));
Remark: delegate void IsActivatedDelegate(); is a delegate declared in their sample's Form1.cs file and CheckIfActivated() is a method declared in the same Form1.cs file.
Now, we know that the Invoke(...) method is from the Control class of System.Windows.Forms namespace. And, hence, cannot be used in a VSTO project. But there may be an alternative for a VSTO project.
UPDATE:
Following is an excerpt from the Form1.cs file of the third party's sample code that is calling Invoke(...) method:
...........
...........
void mnuActDeact_Click(object sender, EventArgs e)
{
if (isGenuine)
{
// deactivate product without deleting the product key allows the user to easily reactivate
try
{
ta.Deactivate(false);
}
catch (ThirdPartyObjectException ex)
{
MessageBox.Show("Failed to deactivate: " + ex.Message);
return;
}
isGenuine = false;
ShowTrial(true);
}
else
{
// Note: you can launch the ThirdPartyObject wizard or you can create you own interface
// launch ThirdPartyObject.exe to get the product key from the user, and activate.
Process TAProcess = new Process
{
StartInfo =
{
FileName = Path.Combine(
Path.GetDirectoryName(Application.ExecutablePath),"ThirdPartyObject.exe")
},
EnableRaisingEvents = true
};
TAProcess.Exited += p_Exited;
TAProcess.Start();
}
}
void p_Exited(object sender, EventArgs e)
{
// remove the event
((Process) sender).Exited -= p_Exited;
// the UI thread is running asynchronous to ThirdPartyObject closing that's why we can't call CheckIfActivated(); directly
Invoke(new IsActivatedDelegate(CheckIfActivated));
}
delegate void IsActivatedDelegate();
void CheckIfActivated()
{
bool isNowActivated = false;
try
{
isNowActivated = ta.IsActivated();
}
catch (ThirdPartyObjectException ex)
{
MessageBox.Show("Failed to check if activated: " + ex.Message);
return;
}
// recheck if activated
if (isNowActivated)
{
isGenuine = true;
ReEnableAppFeatures();
ShowTrial(false);
}
}
............
........
I wish you showed code for context sake... at least a few of the lines...buh I know this. if you have say
YourDelegate yd = new YourDelegate(YourMethod);
you can substitute:
yd.Invoke(new IsActivatedDelegate(CheckIfActivated));
with
yd(new IsActivatedDelegate(CheckIfActivated));

C# SAP GUI 7.4 Scripting existing Application/Connection/Session

I am trying to write a C# program that will automate a user's input into the SAP GUI (currently on version 7400.3.11.3364) using SAP's scripting API. I've done similar things in the past using VBA, but I'm struggling to get it working exactly how I want it to in C#. My end goal is to have a method that opens SAP (if it isn't already running), returns the GuiApplication object of SAP, and leaves SAP open after my program ends. I currently have it working in VBA, and I believe I had it working correctly in C# on a previous project when we were on SAP GUI version 7.3, but I'm not 100% sure
Here is the VBA Function I use:
Public Function GetSapApp() as GuiApplication
Dim Start as Date
If GetObject("SAPGUI") Is Nothing Then
Start = Now()
Shell "C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe"
Do Until Not GetObject("SAPGUI") Is Nothing Or Now > (Start + TimeValue("00:01:00"))
DoEvents
Loop
If GetObject("SAPGUI") Is Nothing Then
MsgBox "Unable to detect SAP Scripting API. Please contact the developer."
End If
Set GetSapApp = GetObject("SAPGUI").GetScriptingEngine
End Function
And here are the different C# methods I've found while googling:
Below is what I'm currently using (the idea was inspired by this: https://www.codeproject.com/Questions/829500/How-do-I-connect-Csharp-to-SAP-GUI), but there are a couple issues with it. It doesn't seem to detect SAP if it is already open. Also, after my program runs, this SAP isn't detectable by any of our VBA macros.
private static GuiApplication GetSapApp()
{
try
{
object SapGuilRot = new CSapROTWrapper().GetROTEntry("SAPGUI");
return SapGuilRot.GetType().InvokeMember("GetScriptingEngine", System.Reflection.BindingFlags.InvokeMethod, null, SapGuilRot, null) as GuiApplication;
}
catch (Exception e)
{
try
{
string SapPath = "C:\\Program Files (x86)\\SAP\\FrontEnd\\SAPgui\\saplogon.exe";
if (File.Exists(SapPath))
{
System.Diagnostics.Process.Start(SapPath);
DateTime StartTime = DateTime.Now;
object SapGuilRot = new CSapROTWrapper().GetROTEntry("SAPGUI");
while (SapGuilRot == null && 30 >= (DateTime.Now - StartTime).TotalSeconds)
{
SapGuilRot = new CSapROTWrapper().GetROTEntry("SAPGUI");
}
return SapGuilRot.GetType().InvokeMember("GetScriptingEngine", System.Reflection.BindingFlags.InvokeMethod, null, SapGuilRot, null) as GuiApplication;
}
else
{
return null;
}
}
catch
{
return null;
}
}
}
Here is another option I've tried (per this answer on another SO post: https://stackoverflow.com/a/14205520/5134861), but it has the same first issue as above (doesn't detect if SAP is currently running), the SAP session that gets opened when you call the OpenConnection method looks different and then closes when my program is done running.
private static GuiApplication GetSapApp()
{
return (GuiApplication)System.Activator.CreateInstance(Type.GetTypeFromProgID("SapGui.ScriptingCtrl.1"));
}
And here is the last option I've tried, but I get a Cannot create ActiveX component error, despite having varified sapfewse.ocx is registered.
private static GuiApplication GetSapApp()
{
object sap = Microsoft.VisualBasic.Interaction.GetObject("SAPGUI", "");
return sap.GetType().InvokeMember("GetScriptingEngine", System.Reflection.BindingFlags.InvokeMethod, null, sap, null) as GuiApplication;
}
Any help and/or suggestions would be greatly appreciated.
Thanks in advance!
I've used:
Microsoft.VisualBasic.Interaction.GetObject("SAPGUISERVER", "");

Getting EnvDTE.DTE instance outside Visual Studio IDE

I am creating a project automation tool in Visual Studio 2013 where I have my own project template and I am trying to add it to an existing solution programatically.I am using the following code in a console application.
EnvDTE.DTE dte = (EnvDTE.DTE)Marshal.GetActiveObject("VisualStudio.DTE.12.0");
string solDir = dte.Solution.FullName;
solDir=solDir.Substring(0, solDir.LastIndexOf("\\"));
dte.Solution.AddFromTemplate(path, solDir+"\\TestProj", "TestProj", false);
It is working when I run the application from Visual Studio IDE. But when I try to run the exe from command prompt, I get the following exception.
Unhandled Exception: System.Runtime.InteropServices.COMException: Operation unav
ailable (Exception from HRESULT: 0x800401E3 (MK_E_UNAVAILABLE))
at System.Runtime.InteropServices.Marshal.GetActiveObject(Guid& rclsid, IntPtr reserved, Object& ppunk)
at System.Runtime.InteropServices.Marshal.GetActiveObject(String progID)
at ProjectAutomation.Console.Program.Main(String[] args)
I want to know whether there is any way to get the active EnvDTE.DTE instance outside Visual Studio IDE .?
Automating an existing Visual Studio instance from an external tool to modify a loaded solution is a bad idea. If you use GetActiveObject(...) and there are two Visual Studio instances launched, how do you know that the correct instance is returned? And what if the user or Visual Studio is doing something with the solution when the user launches the external tool? There are two better approaches:
1) Use an external tool to automate a new Visual Studio instance, load the desired solution and modify it. This can be done even with the VS instance not visible. To create a new instance the proper code is:
System.Type type = Type.GetTypeFromProgID("VisualStudio.DTE.12.0");
EnvDTE.DTE dte = (EnvDTE.DTE) System.Activator.CreateInstance(type);
dte.MainWindow.Visible = true;
...
2) Use a Visual Studio extension, such as a macro (VS 2010 or lower), add-in (VS 2013 or lower) or package (any VS version) to provide a menu item or button toolbar that, when clicked, modifies the currently loaded solution. This prevent the "busy" scenario because if VS is busy the menu item or toolbar button can't be clicked (unless the "busy" operation is asynchronous).
I found an alternative to GetActiveObject, here, where Kiril explains how to enumerate the ROT. There are other examples on MSDN.
Since some SO users don't like links here are the details:
Enumerate all of the processes, named devenv.exe.
Show a list of main window titles. (I strip "Microsoft Visual Studio" off the end)
Ask the user which one they want to use.
Use the process.Id to find an object in the ROT, which I believe is the OP's question. This is done by enumerating the ROT using IEnumMoniker.Next(), which returns monikers and process id's (in the case of VS).
Having found the moniker. Cast the running object to a DTE and off you go.
COM, ROT and Moniker sounded too complex for me, so I was happy to see that the heavy lifting had already been done at the link above.
I had the example working in a couple of minutes. It worked the first time I stepped through with the debugger. But at full speed, I needed to add some sleeps or retries, because it is easy to get an exception from HRESULT: 0x8001010A (RPC_E_SERVERCALL_RETRYLATER))
Also, I replaced the exact match with a regex that tolerates other versions of VS:
Regex monikerRegex = new Regex(#"!VisualStudio.DTE\.\d+\.\d+\:" + processId, RegexOptions.IgnoreCase);
The issue where VS may be busy, with an open dialog, or compiling many projects is common to many applications you might try to force feed key strokes or COM requests. If you get an error retry for a few seconds. Finally, pop up a message box if needed.
Some SO users don't like links because links get broken ;)
Took me over an hour to write my version so might as well post it here. Requires references to envdte and envte80 (from add references / assemblies / extensions). Provides static method for opening a C# file in Visual Studio or Notepad++ as a backup and optionally navigate to a specific line.
using System;
using System.Diagnostics;
using EnvDTE80;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
namespace whatever
{
public static class CsFile
{
public static void Open(string fileName, int? lineNr = null)
{
try
{
OpenFileInVisualStudio(fileName, lineNr);
}
catch
{
try
{
OpenFileInNotePadPlusPlus(fileName, lineNr);
}
catch
{
// Woe is me for all has failed. Somehow show an error.
}
}
}
public static void OpenFileInVisualStudio(string fileName, int? lineNr = null)
{
DTE2 dte = null;
TryFor(1000, () => dte = GetDteByName("VisualStudio.DTE"));
if (dte == null) throw new Exception("Visual Studio not running?");
dte.MainWindow.Activate();
TryFor(1000, () => dte.ItemOperations.OpenFile(fileName));
if (lineNr.HasValue) TryFor(1000, () => ((EnvDTE.TextSelection)dte.ActiveDocument.Selection).GotoLine(lineNr.Value, true));
}
public static void OpenFileInNotePadPlusPlus(string fileName, int? lineNr = null)
{
if (lineNr.HasValue) fileName += " -n" + lineNr.Value.ToString();
Process.Start(#"C:\Program Files (x86)\Notepad++\notepad++.exe", fileName);
}
private static void TryFor(int ms, Action action)
{
DateTime timeout = DateTime.Now.AddMilliseconds(ms);
bool success = false;
do
{
try
{
action();
success = true;
}
catch (Exception ex)
{
if (DateTime.Now > timeout) throw ex;
}
} while (!success);
}
static DTE2 GetDteByName(string name)
{
IntPtr numFetched = Marshal.AllocHGlobal(sizeof(int));
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
IMoniker[] monikers = new IMoniker[1];
IBindCtx bindCtx;
Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
bindCtx.GetRunningObjectTable(out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
{
IBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
if (runningObjectName.Contains(name))
{
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
DTE2 dte = (DTE2)runningObjectVal;
return (dte);
}
}
return null;
}
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
}
}

Check when code run on local machine or on EC2 in Global.asax

I have a code that I want to run in global.asax in ASP.NET. I want this code to run only on localhost and when the code on an EC2 instance, I want it to run another code (the second code should run when I deploy my project on Amazon Web Service EC2 server), how can I do that without using the DEBUG functionality?
To check for whether or not the request is from the local machine, do this:
bool isLocal = HttpContext.Current.Request.IsLocal;
if(isLocal)
{
// Do things that should only be done when request is local here
}
Note: Read HttpRequest.IsLocal documentation for more information.
I guess uyou can pick few environment variables frm these:
Environment Variables (type ENV)
EC2_AMI_ID
EC2_AKI_ID
EC2_ARI_ID
EC2_AMI_MANIFEST_PATH
EC2_PLACEMENT_AVAILABILITY_ZONE
EC2_HOSTNAME
EC2_INSTANCE_ID
EC2_INSTANCE_TYPE
EC2_LOCAL_HOSTNAME
EC2_PUBLIC_HOSTNAME
EC2_PUBLIC_IPV4
EC2_RESERVATION_ID
EC2_SECURITY_GROUPS
RS_EIP
RS_SERVER
RS_SKETCHY
RS_SYSLOG
RS_TOKEN
RS_SERVER_NAME
List taken from here: https://support.rightscale.com/09-Clouds/AWS/FAQs/FAQ_0013_-_What_EC2_environment_variables_are_available_in_RightScripts%3F
I think, that checking for availability of EC2_AMI_ID and EC2_INSTANCE_ID would be enough to answer your questions.
If you are using ASP.NET dev server (VS 2012 and earlier) use this method:
public static bool IsDebugWebServer()
{
if (HttpContext.Current != null && HttpContext.Current.Request != null)
{
return HttpContext.Current.Request.ServerVariables["SERVER_SOFTWARE"] == null || HttpContext.Current.Request.ServerVariables["SERVER_SOFTWARE"] == string.Empty;
}
else
{
return false;
}
}
And if you are using local IIS Express use this:
public static bool IsDebugWebServer()
{
return String.Compare(Process.GetCurrentProcess().ProcessName, "iisexpress") == 0;
}

"Access Denied" error whilst programmatically activating a feature in SharePoint 2010

I am new to SharePoint so I am following some Microsoft Learning Guides. One exercise is to create a feature reciever to modify the Web.Config file.
I detect the feature being activated or deactivated and call the following routine with the appropriate flag.
void setProliferationFlag(bool status)
{
SPWebApplication webApp = SPWebApplication.Lookup(new Uri("http://SharePoint"));
try
{
SPWebConfigModification mySetting = null;
if (status)
{
mySetting = new SPWebConfigModification();
mySetting.Path = "configuration/appSettings";
mySetting.Name = "add [#key='preventProliferation'] [#value='1']";
mySetting.Sequence = 0;
mySetting.Owner = "Lab05Owner";
mySetting.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
mySetting.Value = "<add key='preventProliferation' value='1' />";
webApp.WebConfigModifications.Add(mySetting);
}
else
{
foreach (SPWebConfigModification modification in
webApp.WebConfigModifications)
{
if (modification.Owner == "Lab05Owner")
{
modification.Value = "<add key='preventProliferation' value='0' />";
}
}
}
webApp.Update();
webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
}
catch
{
}
The event receivers work fine. When I activate the feature this code is run, but when it reaches the "webApp.Update()" line it fails with an "Access Denied" error. No other detils on the error. I am not sure to what the access is denied.
I am running in my development environment on my laptop. This is a Sharepoint Server 2010 installation on Window 7.
Regards Tim
Most likely you will need administrative access. Look at the SPSecurity.RunWithElevatedPrivileges method which allows you to execute such actions within the system account's security context.
You will have to run the whole code elevated, that is including opening the SPWebApplication object. You method will then look like this:
void SetProliferationFlag(…)
{
SPSecurity.RunWithElevatedPrivileges(() =>
{
// … your code goes here …
});
}
Please also note, it's a very bad practice to have empty catch clauses in your code. Do always handle all exceptions, at least by logging them and rethrowing.

Categories