No Events when using reflection on Excel Application COM Object - vb.net

I have an Excel template that I use to link to my application via this method:
Excel VBA:
objApp.SetExcelApp Application
Inside my application code, I want to use reflection to hook up to the WorkbookBeforeClose event on the Excel Application so I can do stuff when that event fires:
VB.NET:
Public Sub SetExcelApp(ByRef objExcel As Object)
Dim evWorkbookBeforeClose As Reflection.EventInfo = objExcel.GetType().GetEvent("WorkbookBeforeClose")
Dim mEvents As Reflection.EventInfo() = objExcel.GetType().GetEvents()
' do stuff
End Sub
Surprisingly, evWorkbookBeforeClose is Nothing - even mEvents is Nothing!
I can use methods and properties completely fine. Like objExcel.ActiveWorkbook and objExcel.Run("MyVBAMethod")
Anyone have ideas why this doesn't work? Is this not possible using reflection? Do I have to add a reference to the Excel interop?
EDIT: So if I add the Excel interop as a reference, it will work fine, since I can just use AddHandler on the event. But I'd still like to know why this doesn't work using reflection...
An interesting thing I saw was even when I changed this to objExcel As Microsoft.Office.Interop.Excel.Application, doing the above calls (GetEvent/GetEvents) still both return Nothing!

The events can be added using the System.Runtime.InteropServices.ComEventsHelper class.
E.g.
// source: http://www.guysmithferrier.com/downloads/csharp4.pdf
public static void ExcelExample() {
Type ty = Type.GetTypeFromProgID("Excel.Application");
Object o1 = Activator.CreateInstance(ty); // COM Object
o1.GetType().InvokeMember("Visible", BindingFlags.SetProperty, null, o1, new Object[] { true });
// Sh = Sheet?
// Target = Range
// 0x616 is the SelectionChanged event
ComEventsHelper.Combine(o1, new Guid("00024413-0000-0000-C000-000000000046"), 0x616, new Action<Object,Object>((Sh, Target) => {
int breakPoint = 1;
}));
}
public static void WordExample() {
Type ty = Type.GetTypeFromProgID("Word.Application");
Object o1 = Activator.CreateInstance(ty); // COM Object
o1.GetType().InvokeMember("Visible", BindingFlags.SetProperty, null, o1, new Object[] { true });
//var wdg4 = new Guid("00020A01-0000-0000-C000-000000000046"); // ApplicationEvents4 (Word)
//var wdg3 = new Guid("00020A00-0000-0000-C000-000000000046"); // ApplicationEvents3 (Word)
//var wdg2 = new Guid("000209FE-0000-0000-C000-000000000046"); // ApplicationEvents2 (Word)
var wdg1 = new Guid("000209F7-0000-0000-C000-000000000046"); // ApplicationEventsInterface (Word)
// 2 = Quit event
ComEventsHelper.Combine(o1, wdg1, 2, new Action(() => {
int breakPoint = 1;
}));
// 4 = Document open
ComEventsHelper.Combine(o1, wdg1, 4, new Action<Object>((Doc) => {
int breakPoint = 1;
}));
}
Seems like there is no MSDN reference for the dispatch ids (dispid) values. I found a few sources online, so I'll post them here:
Source: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/3625e60d-0e9c-4871-a0e8-bcedbbb31ce0/is-there-an-fixed-version-of-microsoftofficeinteropworddll-on-msdn?forum=worddev
Word:
[DispId(0x00000001)]
void Startup();
[DispId(0x00000002)]
void Quit();
[DispId(0x00000003)]
void DocumentChange();
[DispId(0x00000004)]
void DocumentOpen(Word.Document Doc);
[DispId(0x00000006)]
void DocumentBeforeClose(Word.Document Doc, ref bool Cancel);
[DispId(0x00000007)]
void DocumentBeforePrint(Word.Document Doc, ref bool Cancel);
[DispId(0x00000008)]
void DocumentBeforeSave(Word.Document Doc, ref bool SaveAsUI, ref bool Cancel);
[DispId(0x00000009)]
void NewDocument(Word.Document Doc);
[DispId(0x0000000a)]
void WindowActivate(Word.Document Doc, Word.Window Wn);
[DispId(0x0000000b)]
void WindowDeactivate(Word.Document Doc, Word.Window Wn);
[DispId(0x0000000c)]
void WindowSelectionChange(Word.Selection Sel);
[DispId(0x0000000d)]
void WindowBeforeRightClick(Word.Selection Sel, ref bool Cancel);
[DispId(0x0000000e)]
void WindowBeforeDoubleClick(Word.Selection Sel, ref bool Cancel);
[DispId(0x0000000f)]
void EPostagePropertyDialog(Word.Document Doc);
[DispId(0x00000010)]
void EPostageInsert(Word.Document Doc);
[DispId(0x00000011)]
void MailMergeAfterMerge(Word.Document Doc, Word.Document DocResult);
[DispId(0x00000012)]
void MailMergeAfterRecordMerge(Word.Document Doc);
[DispId(0x00000013)]
void MailMergeBeforeMerge(Word.Document Doc, int StartRecord, int endRecord, ref bool Cancel);
[DispId(0x00000014)]
void MailMergeBeforeRecordMerge(Word.Document Doc, ref bool Cancel);
[DispId(0x00000015)]
void MailMergeDataSourceLoad(Word.Document Doc);
[DispId(0x00000016)]
void MailMergeDataSourceValidate(Word.Document Doc, ref bool Handled);
[DispId(0x00000017)]
void MailMergeWizardSendToCustom(Word.Document Doc);
[DispId(0x00000018)]
void MailMergeWizardStateChange(Word.Document Doc, ref int FromState, ref int ToState, ref bool Handled);
[DispId(0x00000019)]
void WindowSize(Word.Document Doc, Word.Window Wn);
Excel:
Source: http://www.codeforge.com/read/240027/AppEvents.java__html
#DISPID(1565)
public void newWorkbook(Wb)
#DISPID(1558)
public void sheetSelectionChange(Sh, Target)
#DISPID(1559)
public void sheetBeforeDoubleClick(Sh, Target, Cancel);
#DISPID(1560)
public void sheetBeforeRightClick(Sh, Target, Cancel);
#DISPID(1561)
public void sheetActivate(Sh)
#DISPID(1562)
public void sheetDeactivate(Sh)
#DISPID(1563)
public void sheetCalculate(Sh)
#DISPID(1564)
public void sheetChange(Sh, Target)
#DISPID(1567)
public void workbookOpen(Wb)
#DISPID(1568)
public void workbookActivate(Wb)
#DISPID(1569)
public void workbookDeactivate(Wb)
#DISPID(1570)
public void workbookBeforeClose(Wb, Cancel)
#DISPID(1571)
public void workbookBeforeSave(Wb, SaveAsUI, Cancel)
#DISPID(1572)
public void workbookBeforePrint(Wb, Cancel)
#DISPID(1573)
public void workbookNewSheet(Wb, Sh)
#DISPID(1574)
public void workbookAddinInstall(Wb)
#DISPID(1575)
public void workbookAddinUninstall(Wb)
#DISPID(1554)
public void windowResize(Wb, Window)
#DISPID(1556)
public void windowActivate(Wb, Window)
#DISPID(1557)
public void windowDeactivate(Wb, Window)
#DISPID(1854)
public void sheetFollowHyperlink(Sh, Hyperlink)
#DISPID(2157)
public void sheetPivotTableUpdate(Sh, PivotTable)
#DISPID(2160)
public void workbookPivotTableCloseConnection(Wb, PivotTable)
#DISPID(2161)
public void workbookPivotTableOpenConnection(Wb, PivotTable)
#DISPID(2289)
public void workbookSync(Wb, SyncEventType)
#DISPID(2290)
public void workbookBeforeXmlImport(Wb, XmlMap, Url, IsRefresh, Cancel)
#DISPID(2291)
public void workbookAfterXmlImport(Wb, XmlMap, IsRefresh, Result)
#DISPID(2292)
public void workbookBeforeXmlExport(Wb, XmlMap, Url, Cancel)
#DISPID(2293)
public void workbookAfterXmlExport(Wb, XmlMap, Url, Result)
#DISPID(2611)
public void workbookRowsetComplete(Wb, Description, Sheet, Success)
#DISPID(2612)
public void afterCalculate()

Related

When attaching agent to running process, bytebuddy transformer doesn't seem to take effect

The code of my program to be attached is as below.
public class Foo {
}
public class TestEntry {
public TestEntry() {
}
public static void main(String[] args) throws Exception {
try
{
while(true)
{
System.out.println(new Foo().toString());
Thread.sleep(1000);
}
}
catch(Exception e)
{}
}
}
What I attempt to do is to make Foo.toString() returns 'test' by using the following agent.
public class InjectionAgent {
public InjectionAgent() {
}
public static void agentmain(String args, Instrumentation inst) throws Exception
{
System.out.println("agentmain Args:" + args);
new AgentBuilder.Default()
.type(ElementMatchers.named("Foo"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(Builder<?> arg0, TypeDescription arg1,
ClassLoader arg2, JavaModule arg3) {
return arg0.method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("test"));
}
}).installOn(inst);
}
public static void premain(String args, Instrumentation inst) throws Exception
{
System.out.println("premain Args:" + args);
new AgentBuilder.Default()
.type(ElementMatchers.named("Foo"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(Builder<?> arg0, TypeDescription arg1,
ClassLoader arg2, JavaModule arg3) {
return arg0.method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("test"));
}
}).installOn(inst);
}
}
I notice that, it was successful when I using -javaagent way, whereas attach way failed, here is code for attach.
public class Injection {
public Injection() {
}
public static void main(String[] args) throws AttachNotSupportedException, IOException, AgentLoadException, AgentInitializationException, InterruptedException {
VirtualMachine vm = null;
String agentjarpath = args[0];
vm = VirtualMachine.attach(args[1]);
vm.loadAgent(agentjarpath, "This is Args to the Agent.");
vm.detach();
}
}
I tried to add AgentBuilder.Listener.StreamWriting.toSystemOut() to the agent, after attaching, the output of TestEntry shows
[Byte Buddy] DISCOVERY Foo [sun.misc.Launcher$AppClassLoader#33909752, null, loaded=true]
[Byte Buddy] TRANSFORM Foo [sun.misc.Launcher$AppClassLoader#33909752, null, loaded=true]
[Byte Buddy] COMPLETE Foo [sun.misc.Launcher$AppClassLoader#33909752, null, loaded=true]
Foo#7f31245a
Foo#6d6f6e28
Foo#135fbaa4
Foo#45ee12a7
Foo#330bedb4
==================================Update=====================================
I defined a public method 'Bar' in Foo like this
public class Foo {
public String Bar()
{
return "Bar";
}
}
and then I was trying to make Foo.Bar() returns "modified" in the following way:
public static void agentmain(String args, Instrumentation inst) throws Exception
{
System.out.println("agentmain Args:" + args);
premain(args, inst);
new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges()
.with(AgentBuilder.Listener.StreamWriting.toSystemOut())
.type(ElementMatchers.named("Foo"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(Builder<?> arg0, TypeDescription arg1,
ClassLoader arg2, JavaModule arg3) {
return arg0.visit(Advice.to(InjectionTemplate.class).on(ElementMatchers.named("Bar")));
}
})
.installOn(inst);
}
static class InjectionTemplate {
#Advice.OnMethodExit
static void exit(#Advice.Return String self) {
System.out.println(self.toString() + " " + self.getClass().toString());
self = new String("modified");
}
}
but I got this error:
java.lang.IllegalStateException: Cannot write to read-only parameter class java.lang.String at 1
any suggestions?
It does not seem like you are using redefinition for your agent. You can activate it using:
new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges();
The last part is required on most JVMs (with the notable exception of the dynamic code evolution VM, a custom build of HotSpot). It tells Byte Buddy to not add fields or methods, what most VMs do not support.
In this case, it is no longer possible to invoke the original implementation of a method what is however not required for your FixedValue. Typically, users of Byte Buddy take advantage of Advice when creating an agent that applies dynamic transformations of classes.

MethodDecorator.Fody and getting parameter values

I was wondering if with MethodDecorator it's possible to have the passed parameter during the OnException... that would be great since if I can catch an exception I can also have the passed parameter values
Consider this piece of code
static void Main(string[] args)
{
Worker worker = new Worker();
worker.DoWork(6);
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Assembly | AttributeTargets.Module)]
public class LoggableAttribute : Attribute, IMethodDecorator
{
public void OnEntry(System.Reflection.MethodBase method)
{
var args = method.GetParameters();
var arguments = method.GetGenericArguments();
}
public void OnExit(System.Reflection.MethodBase method)
{
}
public void OnException(System.Reflection.MethodBase method, Exception exception)
{
}
}
and
public class Worker
{
[Loggable]
public void DoWork(int i )
{
}
}
I wish to have 6 on the OnEntry/Nor OnException
Thanks
I know this is an old question, but in case someone stumbles upon this like I did, you can add an Init method and capture the argument values there.
e.g:
public class LoggableAttribute : Attribute, IMethodDecorator
{
private object[] arguments;
public void Init(object instance, MethodBase method, object[] args) {
this.arguments = args;
}
public void OnEntry()
{
// this.arguments[0] would be 6 when calling worker.DoWork(6);
}
}
Check out the example on https://github.com/Fody/MethodDecorator

Arraylist can't add element

Why isn't this working for me?
public class Band {
public void addMember(Musician musician) {
musicians.add(musician);
System.out.println("Muscian: " + musician + "was successfully added");
}
}
public static void main(String[] args) {
Band Beatles = new Band("Beatles");
Beatles.addMember("John Lennon");
}
public class Musician {
private String name;
public Musician(String name, Instrument instrument) {
this.name = name;
Instrument Guitar = instrument;
}
public void play() {
}
}
Beatles.addMember("John Lennon");
should be
Beatles.addMember(new Musician("John Lennon", new Instrument(new Guitar()));
I suspect, without seeing your actual Instrument class, based on your comment that this line should work.
The problem is that you are treating some objects as Strings. You need to create the object out of the class that defines it first.

Display JColorChooser as frame and pass color value to buttons

Hello All and Thanks in advance.
I need JColorChooser to appear as the top panel in my window. I used a button in its place for formatting purposes.(See picture) I also need to pass chosen color value from colorchooser to chosen button in array. I have most of the structure complete but I am still pretty new to Java.
** I would like to also note that I do not need 'ok' or 'cancel' buttons in the colorchooser dialog because once you select a color you should be able to press as many buttons as you would like to change their colors to selected color.
View an image of my program
package main;
public class Main {
public static JPanel ToolPanel = new JPanel();
public static JPanel GridPanel = new JPanel();// Define the Panel that Holds 256 Button Array
public static JPanel FullPanel = new JPanel();
public static JButton button[] = new JButton[256];// Define button array for 256 buttons
private static JColorChooser colorChooser;
public static JButton ColorChooserButton = new JButton("This is where the JColorChooser(colorChooser) should go. ");
public static JFrame MaineWindow = new JFrame("Kola Color Crapper");
final static String ToolPanelString = "Tool Panel";
final static String GridPanelString = "Grid Panel";
public static ActionListener ButtonArrayActionListener = new ActionListener() {
public void actionPerformed(ActionEvent aef) {
if (aef.getSource() instanceof JButton) {
((JButton) aef.getSource()).setBackground(Color.blue);}}};
public static ActionListener ColorChooserButtonActionListener = new ActionListener() {
public void actionPerformed(ActionEvent aeef) {
String st="Welcome";
JOptionPane.showMessageDialog(null,st);
}};
public static void main( String[] args ){//Main (Run)
// setup the gui with buttons
CreateFullPanel();
CreateMaineWindow();
}
//CreateToolPanel() is just used to test formatting
public static void CreateToolPanel(){
ToolPanel.setBorder(BorderFactory.createLineBorder(Color.blue, 2));
ToolPanel.setLayout(new GridLayout(1,0));
ColorChooserButton.addActionListener(ColorChooserButtonActionListener);
ToolPanel.add(ColorChooserButton);
ColorChooserButton.setToolTipText("This is a tool tip.");
}
public static void CreateFullPanel(){
CreateToolPanel();
CreateGridPanel();
//I want to insert CreateColorChooserPanel() where CreateToolPanel() is
// CreateColorChooserPanel();
}
public static void CreateGridPanel() {
GridPanel.setBorder(BorderFactory.createLineBorder(Color.blue, 2));
GridPanel.setLayout(new GridLayout(16, 16, 0, 0));
CreateButtonArray();// Create array of buttons
}
public static void CreateButtonArray(){
for (int i = 0; i < button.length; i++) {
button[i] = new JButton(Integer.toString(i + 1));
button[i].addActionListener(ButtonArrayActionListener);
GridPanel.add(button[i]);
GridPanel.setToolTipText("ToolTIp does not work");
}
}
public static void CreateMaineWindow(){
MaineWindow.setLayout(new GridLayout(2,1));//Main Window is resizable
MaineWindow.add(ToolPanel, ToolPanelString);
MaineWindow.add(GridPanel, ToolPanelString);
MaineWindow.setBounds(300,300,600,400);
MaineWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MaineWindow.pack();// pack all the contents of Main Window
MaineWindow.setVisible(true);//Show Main Window
}
public static void CreateColorChooserPanel(){
}
}

Initialize public static variable in Hadoop through arguments

I have a problem with changing public static variables in Hadoop.
I am trying to pass some values as arguments to the jar file from command line.
here is my code:
public class MyClass {
public static long myvariable1 = 100;
public static class Map extends Mapper<Object, Text, Text, Text> {
public static long myvariabl2 = 200;
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
}
}
public static class Reduce extends Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
}
}
public static void main(String[] args) throws Exception {
col_no = Long.parseLong(args[0]);
Map.myvariable1 = Long.parseLong(args[1]);
Map.myvariable2 = Long.parseLong(args[1]);
other stuff here
}
}
But it is not working, myvariable1 & myvaribale2 always have 100 & 200.
I use Hadoop 0.20.203 with Ubuntu 10.04
What you can do to get the same behavior is to store your variables in the Configuration you use to launch the job.
public static class Map extends Mapper<Object, Text, Text, Text> {
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
Configuration conf = context.getConfiguration();
String var2String = conf.get("myvariable2");
long myvariable2 = Long.parseLong(var2String);
//etc.
}
}
public static void main(String[] args) throws Exception {
col_no = Long.parseLong(args[0]);
String myvariable1 = args[1];
String myvariable2 = args[1];
// add values to configuration
Configuration conf = new Configuration();
conf.set("myvariable1", myvariable1);
conf.set("myvariable2", myvariable2);
//other stuff here
}