C# 如何调用C++dll 类的成员函数?(请大家指教一二)

ldruth28 2008-05-19 02:26:29
C#如何调用C++写的MFC DLL中某一类的成员函数呢?
eg...
C++ MFC DLL 中有一个类的公共成员函数:CDllClass::icanseeu()...

C#应用程序如何调用此函数....

求助....先谢啦!!!
...全文
501 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
niesen111 2011-03-17
  • 打赏
  • 举报
回复
C#如何调用C++写的非 MFC DLL中某一类的成员函数呢?
palmax 2008-10-24
  • 打赏
  • 举报
回复
导出函数最好声明 extern "C"
绿色夹克衫 2008-10-24
  • 打赏
  • 举报
回复
mark
yatobiaf 2008-05-20
  • 打赏
  • 举报
回复
DLL是不会有入口集的,dll的入口集为空,
yatobiaf 2008-05-20
  • 打赏
  • 举报
回复
EntryPoint="mySum",
把这句去掉
ldruth28 2008-05-20
  • 打赏
  • 举报
回复
谢谢高人们的回贴,长进不少....根据名位高人的指点,程序改动如下,不过又出现新问题....
期盼!!!!

C++ MFC DLL
void __declspec(dllexport) __stdcall mySum(char * a)
{
CdllFlashToFlxApp cdll ;
cdll.swf2flxtrans((CString) *a);
return ;

}
并在头文件中做了相应声明

C#应用程序
[DllImport("dllFlashToFlx.dll",EntryPoint="mySum",CharSet=CharSet.Ansi,CallingConvention= CallingConvention.StdCall)]
    public static extern void mySum(ref string a);


private void btn_trans_Click(object sender, System.EventArgs e)
{
OpenFileDialog openfile=new OpenFileDialog ();
openfile.Filter ="(*.*)|*.*";
openfile.FilterIndex=2;
if(openfile.ShowDialog ()==DialogResult.OK )
{
strOpenFile=openfile.FileName ;
}
mySum(ref strOpenFile);
}

运行时报错:
未处理的“System.EntryPointNotFoundException”类型的异常出现在 appFlashToFlx.exe 中。

其他信息: 无法在 DLL dllFlashToFlx.dll 中找到名为 mySum 的入口点。

quanwuling 2008-05-19
  • 打赏
  • 举报
回复
Api函数是构筑Windws应用程序的基石,每一种Windows应用程序开发工具,它提供的底层函数都间接或直接地调用了Windows API函数,同时为了实现功能扩展,一般也都提供了调用WindowsAPI函数的接口, 也就是说具备调用动态连接库的能力。Visual C#和其它开发工具一样也能够调用动态链接库的API函数。.NET框架本身提供了这样一种服务,允许受管辖的代码调用动态链接库中实现的非受管辖函数,包括操作系统提供的Windows API函数。它能够定位和调用输出函数,根据需要,组织其各个参数(整型、字符串类型、数组、和结构等等)跨越互操作边界。

下面以C#为例简单介绍调用API的基本过程:
动态链接库函数的声明
 动态链接库函数使用前必须声明,相对于VB,C#函数声明显得更加罗嗦,前者通过 Api Viewer粘贴以后,可以直接使用,而后者则需要对参数作些额外的变化工作。

 动态链接库函数声明部分一般由下列两部分组成,一是函数名或索引号,二是动态链接库的文件名。
  譬如,你想调用User32.DLL中的MessageBox函数,我们必须指明函数的名字MessageBoxA或MessageBoxW,以及库名字User32.dll,我们知道Win32 API对每一个涉及字符串和字符的函数一般都存在两个版本,单字节字符的ANSI版本和双字节字符的UNICODE版本。

 下面是一个调用API函数的例子:
[DllImport("KERNEL32.DLL", EntryPoint="MoveFileW", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall)]
public static extern bool MoveFile(String src, String dst);

 其中入口点EntryPoint标识函数在动态链接库的入口位置,在一个受管辖的工程中,目标函数的原始名字和序号入口点不仅标识一个跨越互操作界限的函数。而且,你还可以把这个入口点映射为一个不同的名字,也就是对函数进行重命名。重命名可以给调用函数带来种种便利,通过重命名,一方面我们不用为函数的大小写伤透脑筋,同时它也可以保证与已有的命名规则保持一致,允许带有不同参数类型的函数共存,更重要的是它简化了对ANSI和Unicode版本的调用。CharSet用于标识函数调用所采用的是Unicode或是ANSI版本,ExactSpelling=false将告诉编译器,让编译器决定使用Unicode或者是Ansi版本。其它的参数请参考MSDN在线帮助.

 在C#中,你可以在EntryPoint域通过名字和序号声明一个动态链接库函数,如果在方法定义中使用的函数名与DLL入口点相同,你不需要在EntryPoint域显示声明函数。否则,你必须使用下列属性格式指示一个名字和序号。

[DllImport("dllname", EntryPoint="Functionname")]
[DllImport("dllname", EntryPoint="#123")]
值得注意的是,你必须在数字序号前加“#”
下面是一个用MsgBox替换MessageBox名字的例子:
[C#]
using System.Runtime.InteropServices;

public class Win32 {
[DllImport("user32.dll", EntryPoint="MessageBox")]
public static extern int MsgBox(int hWnd, String text, String caption, uint type);
}
许多受管辖的动态链接库函数期望你能够传递一个复杂的参数类型给函数,譬如一个用户定义的结构类型成员或者受管辖代码定义的一个类成员,这时你必须提供额外的信息格式化这个类型,以保持参数原有的布局和对齐。

C#提供了一个StructLayoutAttribute类,通过它你可以定义自己的格式化类型,在受管辖代码中,格式化类型是一个用StructLayoutAttribute说明的结构或类成员,通过它能够保证其内部成员预期的布局信息。布局的选项共有三种:

布局选项
描述
LayoutKind.Automatic
为了提高效率允许运行态对类型成员重新排序。
注意:永远不要使用这个选项来调用不受管辖的动态链接库函数。
LayoutKind.Explicit
对每个域按照FieldOffset属性对类型成员排序
LayoutKind.Sequential
对出现在受管辖类型定义地方的不受管辖内存中的类型成员进行排序。
传递结构成员
下面的例子说明如何在受管辖代码中定义一个点和矩形类型,并作为一个参数传递给User32.dll库中的PtInRect函数,
函数的不受管辖原型声明如下:
BOOL PtInRect(const RECT *lprc, POINT pt);
注意你必须通过引用传递Rect结构参数,因为函数需要一个Rect的结构指针。
[C#]
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public struct Point {
public int x;
public int y;
}

[StructLayout(LayoutKind.Explicit]
public struct Rect {
[FieldOffset(0)] public int left;
[FieldOffset(4)] public int top;
[FieldOffset(8)] public int right;
[FieldOffset(12)] public int bottom;
}

class Win32API {
[DllImport("User32.dll")]
public static extern Bool PtInRect(ref Rect r, Point p);
}
类似你可以调用GetSystemInfo函数获得系统信息:
? using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_INFO {
public uint dwOemId;
public uint dwPageSize;
public uint lpMinimumApplicationAddress;
public uint lpMaximumApplicationAddress;
public uint dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public uint dwProcessorLevel;
public uint dwProcessorRevision;
}


[DllImport("kernel32")]
static extern void GetSystemInfo(ref SYSTEM_INFO pSI);

SYSTEM_INFO pSI = new SYSTEM_INFO();
GetSystemInfo(ref pSI);

类成员的传递
同样只要类具有一个固定的类成员布局,你也可以传递一个类成员给一个不受管辖的动态链接库函数,下面的例子主要说明如何传递一个sequential顺序定义的MySystemTime类给User32.dll的GetSystemTime函数, 函数用C/C++调用规范如下:

void GetSystemTime(SYSTEMTIME* SystemTime);
不像传值类型,类总是通过引用传递参数.
[C#]
[StructLayout(LayoutKind.Sequential)]
public class MySystemTime {
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
class Win32API {
[DllImport("User32.dll")]
public static extern void GetSystemTime(MySystemTime st);
}
回调函数的传递:
从受管辖的代码中调用大多数动态链接库函数,你只需创建一个受管辖的函数定义,然后调用它即可,这个过程非常直接。
如果一个动态链接库函数需要一个函数指针作为参数,你还需要做以下几步:
首先,你必须参考有关这个函数的文档,确定这个函数是否需要一个回调;第二,你必须在受管辖代码中创建一个回调函数;最后,你可以把指向这个函数的指针作为一个参数创递给DLL函数,.

回调函数及其实现:
回调函数经常用在任务需要重复执行的场合,譬如用于枚举函数,譬如Win32 API 中的EnumFontFamilies(字体枚举), EnumPrinters(打印机), EnumWindows (窗口枚举)函数. 下面以窗口枚举为例,谈谈如何通过调用EnumWindow 函数遍历系统中存在的所有窗口

分下面几个步骤:
1. 在实现调用前先参考函数的声明
BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam)
显然这个函数需要一个回调函数地址作为参数.
2. 创建一个受管辖的回调函数,这个例子声明为代表类型(delegate),也就是我们所说的回调,它带有两个参数hwnd和lparam,第一个参数是一个窗口句柄,第二个参数由应用程序定义,两个参数均为整形。

  当这个回调函数返回一个非零值时,标示执行成功,零则暗示失败,这个例子总是返回True值,以便持续枚举。
3. 最后创建以代表对象(delegate),并把它作为一个参数传递给EnumWindows 函数,平台会自动地 把代表转化成函数能够识别的回调格式。

[C#]
using System;
using System.Runtime.InteropServices;

public delegate bool CallBack(int hwnd, int lParam);

public class EnumReportApp {

[DllImport("user32")]
public static extern int EnumWindows(CallBack x, int y);

public static void Main()
{
CallBack myCallBack = new CallBack(EnumReportApp.Report);
EnumWindows(myCallBack, 0);
}

public static bool Report(int hwnd, int lParam) {
Console.Write("窗口句柄为");
Console.WriteLine(hwnd);
return true;
}
}


指针类型参数传递:
 在Windows API函数调用时,大部分函数采用指针传递参数,对一个结构变量指针,我们除了使用上面的类和结构方法传递参数之外,我们有时还可以采用数组传递参数。

 下面这个函数通过调用GetUserName获得用户名
BOOL GetUserName(
LPTSTR lpBuffer, // 用户名缓冲区
LPDWORD nSize // 存放缓冲区大小的地址指针
);
 
[DllImport("Advapi32.dll",
EntryPoint="GetComputerName",
ExactSpelling=false,
SetLastError=true)]
static extern bool GetComputerName (
[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,
  [MarshalAs(UnmanagedType.LPArray)] Int32[] nSize );
 这个函数接受两个参数,char * 和int *,因为你必须分配一个字符串缓冲区以接受字符串指针,你可以使用String类代替这个参数类型,当然你还可以声明一个字节数组传递ANSI字符串,同样你也可以声明一个只有一个元素的长整型数组,使用数组名作为第二个参数。上面的函数可以调用如下:

byte[] str=new byte[20];
Int32[] len=new Int32[1];
len[0]=20;
GetComputerName (str,len);
MessageBox.Show(System.Text.Encoding.ASCII.GetString(str));
 最后需要提醒的是,每一种方法使用前必须在文件头加上:
 using System.Runtime.InteropServices;
  • 打赏
  • 举报
回复
cdll.swf2flxtrans((CString)a); <====很强悍的强制转化.
->
cdll.swf2flxtrans( CString(a) );
ldruth28 2008-05-19
  • 打赏
  • 举报
回复
C++ MFC DLL
extern "C" __declspec(dllexport) void mySum(char a)
{
CdllFlashToFlxApp cdll ;
cdll.swf2flxtrans((CString)a);
return ;

}
Public void CdllFlashToFlxApp::swf2flxtrans(CString filename)

C#
[DllImport("dllFlashToFlx.dll",EntryPoint="mySum",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
  public static extern void mySum(string a);
C#应用程序运行时报错:
未处理的“System.NullReferenceException”类型的异常出现在 appFlashToFlx.exe 中。
其他信息: 未将对象引用设置到对象的实例。
ldruth28 2008-05-19
  • 打赏
  • 举报
回复
我是用第一种方法做的....但是不行...运行时报错
yatobiaf 2008-05-19
  • 打赏
  • 举报
回复
实现不了。对于这个问题,一般有三种解决方法:
一 ,用c函数封装c++类成员函数,记得要在函数定义前加上extern "C",然后到c#中用[dllImport("你的dll名字")]声明调用
二 ,用COM封装
三 ,讲unmanaged c++重写成CLI。
  • 打赏
  • 举报
回复
你用C++/CLI做中转吧.
ldruth28 2008-05-19
  • 打赏
  • 举报
回复
那怎么办,,,有没什么办法可以解决的...如果是一个C++的普通函数就可以调用的,(已实现)一个C++的类成员函数要怎么办?
难不成把C++代码转换为C#代码?....(有没有更好的办法呢)

谢谢!!!
ericzhangbo1982111 2008-05-19
  • 打赏
  • 举报
回复
实现不了。
  • 打赏
  • 举报
回复
C++自己调用都是问题,何况是其他语言
  • 打赏
  • 举报
回复
实现不了
本程序90%代码为作者原创 其余代码均为网络收集使用 版权归原作者所有 程序主要开发语言采用c# 其余的语言也有采用 软件没什么技术含量 本扫描器没有启发引擎 仅仅只有简单的未知文件分析模块 可以有一定的未知病毒判断能力 因为考虑到作者时间和精力等问题 本程序只采用了文本数据库作为病毒库和可信库 病毒库文件夹为bdk 可信库文件夹为kxk 欢迎有志之士继续在这基础上优化进行开发(前提是必须遵守gpl开源协议) 软件纯属原创 为本人自主作品 代码写的很垃圾 各位高手不喜勿喷 仅为思路学习 帮助更多小白学习编程思想 纯属无私奉献 多多指教 作者本人也是半屌子程序员一个 本软件有许多我已知的bug 没修复的原因是时间真的十分有限 都是小bug 还大牛们改改在把源码发出来让大家学习一下 开发本程序的初衷就是开源和学习 至于扫描效果和修复效果说实话还是比较差的 因为那个未知文件分析模块的dll我也没怎么写 至于那些dll都是简单的代码实现 考虑到某些国产“山寨”的产品 盗用别人的成果所以我就不发源码了 如果有开发软件的作者赏脸愿意合作的话我还是愿意改进的 软件采用c#(70%) c/c++(20%)易语言(10%)进行开发。。。。编译环境:vs2013 win8/64位 因为底层技术学的不咋的 病毒就是简单的文件删除和修复下注册表 没什么技术含量 至于我开发的自主云。。。 那个纯属就是是tcp协议的网络通讯然后在服务器上查找数据库 也没多大的技术含量 服务器是我买的vps 还是花了我不少钱的 服务器在北京 速度应该不会慢 预计半年内服务器的云不会消失 用本引擎diy杀软的朋友不必担心 如果需要我这菜鸟的技术支持等联系我QQ1620493525(加的时候注明一下谢谢了!) 如果有什么好玩的c语言/c#语言的项目拉上我打打酱油我也是相当开心的(易语言的项目就算了。。。) 本软件是我创建的第一个开源项目 转载注明作者的联系方式以及gpl开源协议 大牛们不喜勿喷。。。。仅为娱乐学习 重在学习 让大家都可以学到基础的编程知识和杀毒知识何乐而不为呢? 介于本人技术实在不怎么样。。。最近在开发一款脚本语言(用纯c) 这款脚本语言主要是为了解决windows方面的一些问题(特点:简单 高效 ) 可以用来迅速编写病毒专杀等工具(当然是我自己发明的语法。。。)是全英文的 但是肯定会比易语言简单,直接 直接进行c解释器效率应该不会很低 因为是低级的小脚本语言所以大家别见笑。。。如果有可以合作的志同道合的朋友可以联系我一起合作。。。。 另外本扫描器估计禁不起评测 查杀率略低了。。也没有启发 所以大家学习参考就行了。。。。 如果有哪个软件赏脸采用本引擎务必写上本人的版权(QQ等等

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧