前言:调用dll时,需要先问清楚dll是由什么语言编译的,要不然方向错了会有很多无用功
试错过程:
- 直接引用出错:“…不是规范的COM组件”
- DllImportm方式
[DllImport("yhinterface.dll", EntryPoint = "f_sblwsk", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
public static extern void f_sblwsk(string hospitalComb, ref object ret);
更详细的可见c#调用外部dll
使用以上方法遇到的报错
- 直接引用时,报错“尝试写入或读取受保护的内存,这通常是指内存已损坏”
- DllImport时,报错无法找到方法入口
- 无法加载该模块
解决方案思路:
- 最开始由于不知道dll是pb生成的,误当作c++生成的,所以当报错无法找到入口时,就怀疑提供方给错dll了,当时接口文档也没有说清楚,就一直在找这个问题。
- 后来注册了该dll,把相关的依赖项放进与exe同级目录下,就可以直接引用进去了
- 之后直接调用的时候,需要new一个对象,用过两种方式
- 常见new
COClass_n_yhinterface b = new COClass_n_yhinterface();
- 反射
```csharp
private object Invoke(string lpFileName, string Namespace, string ClassName, string lpProcName, object[] ObjArray_Parameter)
{
try
{ // 载入程序集
Assembly MyAssembly = Assembly.LoadFrom(lpFileName);
Type[] type = MyAssembly.GetTypes();
foreach (Type t in type)
{// 查找要调用的命名空间及类
if (t.Namespace == Namespace && t.Name == ClassName)
{// 查找要调用的方法并进行调用
MethodInfo m = t.GetMethod(lpProcName);
if (m != null)
{
object o = Activator.CreateInstance(t);
return m.Invoke(o, ObjArray_Parameter);
}
else Console.WriteLine(" 装载出错 !");
}
}
}
catch (System.NullReferenceException e)
{
Console.WriteLine(e.Message);
}//catch
return (object)0;
}
以上方法仍然报“…内存损坏”
-
开始怀疑dll不是c++编译的,于是找反编译工具尝试
我找了两种,一种是depends22_x86,一种是pb反编译工具;通过depends22_x86可以看到dll的函数入口,并没有需要的函数,反编译工具编译出pb程序以后,就开始找新方向了;
pb的dll注册:需要pb运行环境,PB环境变量不需要配,把PB的支持库文件放到system32里就行了;也可以安装一下powerbuilder;或者把pb的*.dll和.exe放一起就可以跑PB程序了
参考:
C#调用PB写的com组件dll
C#WEBSERVICE调用PB生成的DLL -
出结果以后还需注意
m_Com_Document.InvokeMember("f_sblwsk", BindingFlags.InvokeMethod, null, objDoc, new object[] { hospitalComb, RetStr });
这种方式,如果函数方法是出参ref或者out 类型时,参数值并未发生变化
解决方案:
object[] args = new object[2];
string RetStr = "123";
args[0] = hospitalComb;
args[1] = RetStr;
ParameterModifier pMod = new ParameterModifier(2);
pMod[1] = true;
ParameterModifier[] mods = { pMod };
Type m_Com_Document = Type.GetTypeFromProgID("PB90.n_yhinterface");
object objDoc = Activator.CreateInstance(m_Com_Document);
object tempObj = m_Com_Document.InvokeMember("f_sblwsk", BindingFlags.InvokeMethod, null, objDoc, args,mods,null,null);
Console.WriteLine("args[1]:" + Convert.ToString(args[1]));
Console.WriteLine("RetStr:" + Convert.ToString(RetStr));
Console.WriteLine("tempObj:" + Convert.ToString(tempObj));
RetStr = Convert.ToString(args[1]);
string[] datas = RetStr.Split('|');
Console.WriteLine("datas[0]:" + datas[0]);
参考:
使用数组
使用ParameterModifier
以上,解决
后续需要加强的部分:
- 反射
- 值类型和引用类型,值的变化
- wcf和wpf的理解
- 以上的理解会造成,有时已经找到答案了,因为认知缺陷就把正确答案pass掉了,也做了很多无用功,看网上的资料时,遇到别人的解决方案,需要耐心验证,不要走马观花