通过反射调用COM对象

声明:原作者小李菜刀(caidaoli)

发一篇以前写的一个旧文,申请Blog好久了,要是再不发东西可就被Kill了。
      在.net中使用Com对象大家第一个反应就是在工程中引用Com对象,让vs.net自动生成一个包装过的.net类库。这种方法虽然方便,但是有很明显的缺点,最致命的就是开发的机器上安装的Com对象的版本比客户机器上安装的高,开发的程序无法正确的运行。如开发机器安装的是office 2003,而客户机器上的安装的确是office 2000、office xp甚至还有安装的是Office97,这种情况我想大家都深有体会吧?    
      在非托管语言如Delphi中可以通过CreateComObject来生成一个Com对象,然后再使用Variant对象去调用Com对象的属性、方法。可因C#是一种强类型的语言,他没有象Delphi中的Variant这中可变类型,因此只能通过反射去完成。
      晚上心血来潮就封装了一个Variant对象来模拟一下Delphi中的Variant。

 示例代码:

             // 生成Excel对象
             using  (Variant excel  =  Variant.CreateComInstance( " Excel.Application " ))
            
{
                
//显示Excel界面
                excel["Visible"= true;
                
//获取workBooks对象
                using (Variant workBooks = Variant.CreateInstance(excel["Workbooks"]))
                
{
                    
//建立一个Workbook
                    using (Variant workBook = Variant.CreateInstance(workBooks.InvokeMethod("Add")))
                    
{

                        
for (int i = 1; i < 10; i++)
                        
{
                            
for (int j = 1; j < 10; j++)
                            
{
                                
//根据指定的索引获得一个单元格
                                using (Variant cell = Variant.CreateInstance(excel["Cells", i, j]))
                                
{
                                    
//设置单元格的值
                                    cell["Value"= i * j;
                                    Console.WriteLine(cell[
"Value"]);
                                }

                            }

                        }

                    }

                }

                
//excel.InvokeMethod("Quit");
            }


这样的方式是不是爽多了?那些讨厌的可选参数终于不用再写了!

 

Variant类源码:

 

Using directives

class  Variant : IDisposable        
{
    Variant() 
{ }
    
object _obj;
    Type _objType;
    
bool _isComObj = false;

    
public static Variant CreateComInstance(string progID)
    
{
        Variant variant 
= new Variant();
        variant._objType 
= Type.GetTypeFromProgID(progID);
        variant._obj 
= Activator.CreateInstance(variant._objType);
        
        variant._isComObj 
= true;
        
return variant;
    }

    
public static Variant CreateInstance(string objectTypeName)
    
{
        Variant variant 
= new Variant();
        variant._objType 
= Type.GetType(objectTypeName);
        variant._obj 
= Activator.CreateInstance(variant._objType);
        
return variant;
    }

    
public static Variant CreateInstance(object obj)
    
{
        Variant variant 
= new Variant();
        variant._objType 
= obj.GetType();
        variant._obj 
= obj;
        variant._isComObj 
= obj.GetType().ToString() == "System.__ComObject";
        
return variant;
    }

    
public int ReleaseComObject()
    
{
        
try
        
{
            
if (_isComObj)
                
return Marshal.ReleaseComObject(_obj);
            
else
                
throw new Exception("_obj is not com Object");
        }

        
catch
        
{
            
throw;
        }

    }

    
public object InvokeMethod(string methodName,params object[] parameters)
    
{
        
try
        
{
            
return _objType.InvokeMember(methodName,
                BindingFlags.InvokeMethod,
                
null, _obj, parameters);
        }

        
catch
        
{
            
throw;
        }

    }


    
    
public object GetPropertyValue(string propertyName, params object[] parameters)
    
{
        
try
        
{
            
return _objType.InvokeMember(propertyName,
                BindingFlags.GetProperty,
                
null, _obj, parameters);
        }

        
catch
        
{
            
throw;
        }

    }

    
public void SetPropertyValue(string propertyName, object value, params object[] parameters)
    
{
        
if (value as DBNull != null)
            value 
= null;
        
string s = value as string;
        
if (s != null && s.Length == 0)
            value 
= null;

        
if (_isComObj)
        
{
            
try
            
{
                
object[] args = new object[parameters.Length + 1];
                Array.Copy(parameters, 
0, args, 1, parameters.Length);
                args[
0= value;
                _objType.InvokeMember(propertyName,
                    BindingFlags.SetProperty,
                    
null, _obj, args);
            }

            
catch
            
{
                
throw;
            }

            
return;
        }


        PropertyInfo pi 
= _objType.GetProperty(propertyName);

        
if (pi != null)
        
{
            
if (pi.CanWrite)
            
{
                
try
                
{
                    pi.SetValue(_obj, 
null == value ?
                        
null :
                        (pi.PropertyType.IsInterface 
?
                            value :
                            Convert.ChangeType(value, pi.PropertyType)
                        ),
                        parameters);
                }

                
catch (Exception E)
                
{
                    
throw new Exception(String.Format("设置属性{0}时出错!属性的类型是{1}值的类型是{2} 错误信息:{3}", pi.Name, pi.PropertyType.FullName, value.GetType().FullName, null == E.InnerException ? E.Message : E.InnerException.Message));
                }

            }

            
else
                
throw new Exception(pi.PropertyType.ToString());
        }

        
else
            
throw new Exception(String.Format("{0}属性未在{1}中定义!", pi.Name, _obj.GetType().FullName));
    }


    
public object this[string propertyName, params object[] parameters]
    
{
        
get return GetPropertyValue(propertyName, parameters); }
        
set { SetPropertyValue(propertyName, value, parameters); }
    }


    
IDisposable Members
}

 

其实Varaint类不光可以调用Com对象,对于托管对象也可以动态的创建和调用。

欢迎大家斧正!

 
<think>好的,我现在要解决用户的问题:如何通过反射调用Java中的main方法,并提供示例。首先,我需要回顾一下Java反射机制的基本知识,特别是如何获取方法调用方法的部分。 用户提到了他们之前看到的引用内容,其中有一个关于反射调用静态方法的例子,还有创建对象的示例。不过用户现在的问题具体是调用main方法,这通常是一个静态方法,所以需要用到反射中的Method类。 首先,我需要确认main方法的签名。在Java中,main方法的定义是public static void,参数是一个String数组。所以在反射时,需要正确指定参数类型,即String[].class。这里可能会有一个注意点,就是参数类型的处理,因为直接使用String[].class可能不够,或者需要使用Object.class来处理可变参数的情况? 然后,获取main方法的步骤应该是:获取类的Class对象,然后通过getMethod或getDeclaredMethod来获取main方法。由于main方法是public的,所以应该使用getMethod("main", String[].class)。这里需要确保参数类型正确,否则会抛出NoSuchMethodException。 接下来是调用方法。因为main是静态方法,所以调用时不需要传入对象实例,第一个参数可以为null。然后传递参数,这里需要注意,invoke方法的参数是一个Object数组,而main方法的参数是一个String数组,所以需要将String数组包装成Object数组的元素。例如,new Object[]{ new String[]{"arg1", "arg2"} }。但是这里可能会有个常见的错误,如果直接传递String数组,可能会因为Java的可变参数导致类型不匹配,所以正确的方式是将String数组作为Object数组的一个元素。 然后,用户可能需要一个完整的代码示例,包括如何处理异常,比如ClassNotFoundException、NoSuchMethodException、InvocationTargetException等。需要确保代码的健壮性。 另外,需要考虑到用户可能对反射机制不熟悉,所以代码中的注释也很重要,解释每一步的作用。比如,获取Class对象,获取main方法,设置可访问性(虽然main是public的,可能不需要),然后调用方法。 还需要提醒用户,main方法的参数是String数组,在调用时要正确传递参数,否则可能出现参数类型不匹配的错误。例如,使用new Object[]{ new String[]{} }来传递空的参数列表。 最后,总结步骤:获取Class对象、获取Method对象调用invoke方法,并处理可能的异常。同时,提供示例代码,并指出关键点,比如参数类型和传递方式。 可能还需要考虑其他情况,比如main方法所在的类是否加载,是否需要初始化类,但一般来说,调用静态方法时会触发类初始化,除非已经加载过了。不过这可能超出用户当前问题的范围,暂时不需要深入。 现在,整合这些思考,形成一个结构清晰的回答,包含步骤说明和代码示例,并指出需要注意的地方,比如参数处理。同时,根据用户提供的引用内容,虽然引用中的例子是关于调用普通方法和构造函数的,但可以借鉴反射的基本流程。</think>### 通过反射调用Java中的main方法分步说明 #### 1. 获取目标类的Class对象 通过完整类名字符串加载目标类: ```java Class<?> clazz = Class.forName("com.example.TestClass"); ``` 若类不存在会抛出`ClassNotFoundException`[^2]。 #### 2. 获取main方法对象 指定方法签名:方法名`main` + 参数类型`String[].class`: ```java Method mainMethod = clazz.getMethod("main", String[].class); ``` 若方法不存在会抛出`NoSuchMethodException`[^2]。 #### 3. 调用静态方法 静态方法调用时实例参数传`null`,参数需封装为`Object`数组: ```java mainMethod.invoke(null, (Object) new String[]{"arg1", "arg2"}); ``` 此处需注意参数强制转型,避免参数类型匹配错误[^1]。 ### 完整示例代码 ```java import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectMain { public static void main(String[] args) { try { // 加载包含main方法的类 Class<?> clazz = Class.forName("com.example.TestClass"); // 获取main方法(注意参数类型声明) Method mainMethod = clazz.getMethod("main", String[].class); // 调用静态方法(第一个参数传null) mainMethod.invoke(null, (Object) new String[]{"参数1", "参数2"}); } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } } ``` ### 关键注意事项 1. **参数类型精确匹配** `getMethod()`必须明确声明`String[].class`作为参数类型,直接使用`String.class`会导致找不到方法 2. **参数包装规范** 调用时需将实际参数包装为`Object`类型: ```java (Object) new String[]{...} ``` 避免被解析为多个参数导致`IllegalArgumentException` 3. **异常处理** - `InvocationTargetException`:被调用的main方法内部抛出异常时触发 - `IllegalAccessException`:方法访问权限不足时触发(main是public方法,通常不会发生) ### 应用场景 1. 动态加载测试入口 2. 实现Java命令行工具 3. 框架中的插件机制
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值