PInvoke——一个强大而又不太为人所知的编程技术

随着软件开发的不断进步,我们今天可以做到的事情昨天或者是前几年是不可能的,但是很多时候这些进步并不为我们所知,而PInvoke就是其中一个值得我们深入了解的技术。

一、PInvoke库

PInvoke(Platform Invoke) 是一个强有力的 .NET Framework 功能,它提供了一种在 .NET 应用程序中调用非托管 DLL 函数的方法。PInvoke 的一项主要功能就是能够让 .NET Framework 应用程序直接使用基于 C 或 C++ 的非托管代码,而这些代码通常是通过相应 DLL 导出的。

在实际编程中,我们会遇到很多需要调用非托管代码的情况。比如,我们需要调用操作系统的 API 功能,调用硬件驱动程序的函数等等。这些非托管代码往往是以 C 或 C++ 等语言编写的,它们不受 .NET Framework 的管理,与 .NET 应用程序的运行环境也是非常不同的。因此,我们在使用这些非托管代码时,需要借助 PInvoke 技术,实现 .NET 应用程序与这些非托管代码之间的互操作。


[DllImport("User32.dll")]
static extern int MessageBox(int h, string m, string c, int type);

以上是一个C# 调用MessageBox的示例,包含DllImport Attribute,它用于指定需要哪个动态链接库(DLL)和其内部函数,在应用程序中使用 unmanaged API 。使用 DllImport 可以达到以下目的:

  • 将参数从一个数据类型转化到另一个数据类型
  • 指定 DLL 文件和函数的入口点,因为它们是 unmanaged 的
  • 返回调用相关 API 函数的结果。

二、PInvoke gdi32

GDI+是图形设备界面(GDI)内置图形渲染引擎的后续版本,最近的版本是 DirectX。

下面的 P/Invoke 函数用于打开并显示图像文件:


[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hDc);
 
public static void DrawImg()
{
    OpenFileDialog openFileDialog = new OpenFileDialog();
    openFileDialog.Filter = "Image Files(*.bmp;*.jpg;*.jpeg;*.png;)|*.bmp;*.jpg;*.jpeg;*.png;";//过滤器
    if (openFileDialog.ShowDialog() == DialogResult.OK)
    {
        using (Bitmap bitmap = new Bitmap(openFileDialog.FileName))
        {
            IntPtr hBitmap = bitmap.GetHbitmap();
            try
            {
                using(Graphics g = Graphics.FromHwnd(IntPtr.Zero))
                {
                    IntPtr hDC = g.GetHdc();
                    try
                    {
                        using( Graphics imageGrpahics = Graphics.FromImage(bitmap))
                        {
                            IntPtr hdcImage = imageGrpahics.GetHdc();
                            try
                            {
                                BitBlt(hDC, 0, 0, bitmap.Width, bitmap.Height, hdcImage, 0, 0, SrcCopy);
                            }
                            finally
                            {
                                imageGrpahics.ReleaseHdc(hdcImage);
                            }
                        }
                    }
                    finally
                    {
                        g.ReleaseHdc(hDC);
                    }
                }
            }
            finally
            {
                DeleteObject(hBitmap);
            }
        }
    }
}

以上代码可以在 openfiledialog 中选取图片文件,并将其显示在程序界面中。

三、PInvoke DLL

P/Invoke 在非托管代码中调用托管(.Net)代码,通过 C++/CLI 支持实现混合编程,通过导出 C++ 静态类导出成非托管 DLL 供其他工程(包括非 .Net 工程)引用,从而实现托管代码的动态链接。

下面是一个使用 P/Invoke 的示例:

在 Visual Studio 中,创建一个新的 C++ CLR 项目,我们可以编写以下代码导出一个以 System.String 为参数,返回 System.Int32 的函数 Multiply:


#include "pch.h"
#include "tchar.h" 
 
using namespace System;
 
namespace ExampleCppDll
{
    public ref class CppWrapper
    {
    public:
        static int Multiply(String^ operand)
        {
            try
            {
                int i_Result = 1;
                for each(Char ch in operand)
                {
                    i_Result *= Convert::ToInt32(ch.ToString());
                }
                return i_Result;
            }
            catch(...)
            {
                return -1;
            }
        }
     };
}
 
extern "C"
{
    __declspec(dllexport) 
    int __stdcall Multiply(LPCTSTR operand)
    {
        return ExampleCppDll::CppWrapper::Multiply(gcnew String(operand));
    }
}

然后编译生成 DLL ,在C# 中,我们可以使用 P/Invoke 调用刚刚写的 Multiply 函数:


[DllImport("ExampleCppDll.dll", CharSet = CharSet.Unicode)]
public static extern int Multiply(string strNumbers); 

四、PInvoke vb6

通过使用 PInvoke 在 VB6 中调用 unmanaged 代码。

下面是一个使用 PInvoke 的示例:


Private Declare Function GetVersionEx Lib "kernel32" Alias "GetVersionExA" ( _
    ByRef lpVersionInformation As OSVERSIONINFO _
) As Long
 
Private Type OSVERSIONINFO
    dwOSVersionInfoSize As Long
    dwMajorVersion As Long
    dwMinorVersion As Long
    dwBuildNumber As Long
    dwPlatformId As Long
    szCSDVersion As String * 128       ' Size of string buffer in Bytes
End Type
 
Private Sub Form_Load()
    Dim os_version As OSVERSIONINFO
    os_version.dwOSVersionInfoSize = LenB(os_version)
    Call GetVersionEx(os_version)
    Debug.Print "Windows version: " & os_version.dwMajorVersion & "." & os_version.dwMinorVersion
End Sub 

以上代码可以在 VB6 中调用 GetVersionEx 函数,获取 Windows 版本信息,并输出到调试窗口中。

五、PInvoke文档

P/Invoke 的参考文档支持许多方面:

  • MDSN 使用 MSDN Library 搜索或 MSDN 中的 P/Invoke 索引
  • Pinvoke.net 它是开发人员社区,提供 P/Invoke 不断更新的文档库
  • PInvoke Interop Assistant 可以帮助您将 C、C 和 C++ 头文件转换为 C# PInvoke 或 VB declarative DLL 函数声明。

六、PInvoke技术

你可以通过PInvoke 技术将合适的编程语言组合实现所需的操作,同时明白一些坑点。

以下是一些 PInvoke 技术示例:

  • 使用 PInvoke 和 Callback 委托将 Windows 程序代码转换为 C# 中的代码
  • 在嵌入式 Windows CE 中使用 PInvoke 和 .Net Compact Framework / Windows Mobile
  • 在 WPF 中使用 PInvoke、SetWindowLong 和 HwndSource 实现 WindowTransparency
  • PCreate ProcessAsUser with SeIncreaseQuotaPrivilege

七、PInvokeStackImbalance

PInvokeStackimbalance 是对于 P/Invoke 传递参数而产生的错误类型的名字,主要由于目标函数参数数目或类型不匹配而导致。针对该错误,可以通过调整P/Invoke 声明中的参数数量或类型来解决。

以下是解决栈平衡错误的 P/Invoke 声明示例:


[DllImport("User32.dll")]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

在调用 MoveWindow 时,需要设置第 5 个参数为 bool 类型而不是 int 类型,以下是正确的声明:


[DllImport("User32.dll")]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, bool bRepaint);

八、PInvoke openfiledialog

PInvoke OpenFileDialog

以下是 P/Invoke OpenFileDialog 的示例代码:


[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr GetForegroundWindow();
 
[STAThread]
static void Main()
{
    var opDlg= new OpenFileDialog();
    opDlg.ShowDialog(GetForegroundWindow);
}

以上代码通过 P/Invoke OpenFileDialog 在提示框架之间提供弹出式窗口。

结论

通过 PInvoke,在非托管代码中调用托管代码,我们可以更简单的处理 unmanaged 代码。无论我们想在 .Net 中调用现有的 C/C++ 动态链接库或使 C# 代码跨越语言边界工作,在经验和技术上,P/Invoke 都是不可或缺的一项功能。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/282604.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-22 08:05
下一篇 2024-12-22 08:05

相关推荐

  • java编程技术一,编程基础java

    本文目录一览: 1、学java编程技术必须得会英语吗? 2、JAVA编程技术是什么 3、java编程抽象类技术如何实现? 4、java编程技术是用来编写程序的吗? 5、JAVA的技…

    编程 2025-01-13
  • jsp动态网页编程技术课件下载,jsp网页设计作业

    本文目录一览: 1、洪恩软件 2、请大家介绍下关于JSP和J2EE方面的书 3、谁提供些学习软件和学习网站或游戏————加分的哦 4、在哪里能下载authorware教学课件实例?…

    编程 2024-12-30
  • jsp编程技术第二版课后答案,jsp编程技术课后答案徐天凤

    本文目录一览: 1、请问大虾们 2、JSP 编程题 希望知道的人能提供正确答案! 3、求英语文献。关于JSP技术的。 4、新闻发布系统的论文 5、jsp图片插入数据库并读出页面 6…

    编程 2024-12-28
  • javaweb编程,javaweb编程技术第三版答案

    本文目录一览: 1、JAVA、web主要学什么内容 2、北大青鸟java培训:web前端编程该怎样学习? 3、java,web程序设计要学些什么? 4、北大青鸟java培训:web…

    编程 2024-12-14
  • 横扫java基础核心技术(Java核心编程技术)

    本文目录一览: 1、Java的核心技术什么? 2、关于java核心技术和java2核心技术 3、Java的核心技术是什么? 4、java都学什么,具体点的? 5、java的核心技术…

    编程 2024-12-12
  • wic语言编程,c语言编程技术

    本文目录一览: 1、C语言编程的while语句问题~ 2、C语言编程,要求有反代码,功能不限,20行以上,求高手帮忙啊 3、c语言编写一个程序,在电脑上读取单片机用Wi-Fi返回的…

    编程 2024-12-12
  • java编程技术全接触(Java高级编程技术)

    本文目录一览: 1、java应该怎么学习呢? 2、想要自学JAVA,应该如何入门? 3、关于java里静态上下文中的动态变量 4、Java程序员应该掌握哪些技术点 java应该怎么…

    编程 2024-12-10
  • mysql编程技术,Mysql编程

    本文目录一览: 1、mysql主要技术特点 2、mysql编程技术在实际工作中用的多吗?条件变量、存储过程、触发器之类的…… 3、Mysql是什么??? mysql主要技术特点 M…

    编程 2024-11-14
  • JavaBool – Java编程技术交流社区

    一、JavaBool – Java编程技术交流社区介绍 JavaBool是一个致力于Java编程技术交流的社区,提供了丰富的Java编程技术资源和交流平台。无论你是Ja…

    编程 2024-11-11
  • Java高级编程,java高级编程技术

    本文目录一览: 1、Java高级编程有哪些知识呀 2、Java软件工程师一般要上哪些课程? 3、北大青鸟java培训:零基础如何学懂Java语言? 4、学Java的好处有哪些? J…

    编程 2024-10-03

发表回复

登录后才能评论