C#調用Windows API(示例:顯示任務管理器里的程式名稱)

来源:https://www.cnblogs.com/vic-tory/archive/2020/02/25/12362026.html
-Advertisement-
Play Games

作為初學者來說,在C#中使用API確是一件令人頭疼的問題。 在使用API之前你必須知道如何在C#中使用結構、類型轉換、安全/不安全代碼,可控/不可控代碼等許多知識。 在.Net Framework SDK文檔中,關於調用Windows API的指示比較零散,並且其中稍全面一點的是針對Visual B ...


作為初學者來說,在C#中使用API確是一件令人頭疼的問題。

在使用API之前你必須知道如何在C#中使用結構、類型轉換、安全/不安全代碼,可控/不可控代碼等許多知識。

在.Net Framework SDK文檔中,關於調用Windows API的指示比較零散,並且其中稍全面一點的是針對Visual Basic .net講述的。

一.調用格式

 

using System.Runtime.InteropServices; //引用此名稱空間

//使用DllImportAttribute特性來引入api函數,註意聲明的是空方法,即方法體為空。
 [DllImport("user32.dll")] public static extern ReturnType FunctionName(type arg1,type arg2,...);  

DllImportAttribute特性簡介

 

 

 

1、CallingConvention 指示向非托管實現傳遞方法參數時所用的 CallingConvention 值。

  CallingConvention.Cdecl : 調用方清理堆棧。它使您能夠調用具有 varargs 的函數。
  CallingConvention.StdCall : 被調用方清理堆棧。它是從托管代碼調用非托管函數的預設約定。

2、CharSet 控制調用函數的名稱版本及指示如何向方法封送 String 參數。

  此欄位被設置為 CharSet 值之一。如果 CharSet 欄位設置為 Unicode,則所有字元串參數在傳遞到非托管實現之前都轉換成 Unicode 字元。這還導致向 DLL EntryPoint 的名稱中追加字母“W”。如果此欄位設置為 Ansi,則字元串將轉換成 ANSI 字元串,同時向 DLL EntryPoint 的名稱中追加字母“A”。大多數 Win32 API 使用這種追加“W”或“A”的約定。如果 CharSet 設置為 Auto,則這種轉換就是與平臺有關的(在 Windows NT 上為 Unicode,在 Windows 98 上為 Ansi)。CharSet 的預設值為 Ansi。CharSet 欄位也用於確定將從指定的 DLL 導入哪個版本的函數。CharSet.Ansi 和 CharSet.Unicode 的名稱匹配規則大不相同。

  對於 Ansi 來說,如果將 EntryPoint 設置為“MyMethod”且它存在的話,則返回“MyMethod”。如果 DLL 中沒有“MyMethod”,但存在“MyMethodA”,則返回“MyMethodA”。對於 Unicode 來說則正好相反。如果將 EntryPoint 設置為“MyMethod”且它存在的話,則返回“MyMethodW”。如果 DLL 中不存在“MyMethodW”,但存在“MyMethod”,則返回“MyMethod”。如果使用的是 Auto,則匹配規則與平臺有關(在 Windows NT 上為 Unicode,在 Windows 98 上為 Ansi)。如果 ExactSpelling 設置為 true,則只有當 DLL 中存在“MyMethod”時才返回“MyMethod”。

3、EntryPoint 指示要調用的 DLL 入口點的名稱或序號。

  如果你的方法名不想與api函數同名的話,一定要指定此參數,例如:

[DllImport("user32.dll",CharSet="CharSet.Auto",EntryPoint="MessageBox")]
public static extern int MsgBox(IntPtr hWnd,string txt,string caption, int type);

 

4、ExactSpelling 指示是否應修改非托管 DLL 中的入口點的名稱,以與 CharSet 欄位中指定的 CharSet 值相對應。如果為 true,則當 DllImportAttribute.CharSet 欄位設置為 CharSet 的 Ansi 值時,向方法名稱中追加字母 A,當 DllImportAttribute.CharSet 欄位設置為 CharSet 的 Unicode 值時,向方法的名稱中追加字母 W。此欄位的預設值是 false。

5、PreserveSig 指示托管方法簽名不應轉換成返回 HRESULT、並且可能有一個對應於返回值的附加 [out, retval] 參數的非托管簽名。

6、SetLastError 指示被調用方在從屬性化方法返回之前將調用 Win32 API SetLastError。 true 指示調用方將調用 SetLastError,預設為 false。運行時封送拆收器將調用 GetLastError 並緩存返回的值,以防其被其他 API 調用重寫。用戶可通過調用 GetLastWin32Error 來檢索錯誤代碼。

二、參數類型:

  1、數值型直接用對應的就可。(DWORD -> int , WORD -> Int16)

  2、API中字元串指針類型 -> .net中string

  3、API中句柄 (dWord) -> .net中IntPtr

  4、API中結構 -> .net中結構或者類。註意這種情況下,要先用StructLayout特性限定聲明結構或類

  公共語言運行庫利用StructLayoutAttribute控制類或結構的數據欄位在托管記憶體中的物理佈局,即類或結構需要按某種方式排列。如果要將類傳遞給需要指定佈局的非托管代碼,則顯式控制類佈局是重要的。它的構造函數中用LayoutKind值初始化 StructLayoutAttribute 類的新實例。 LayoutKind.Sequential 用於強制將成員按其出現的順序進行順序佈局。

  LayoutKind.Explicit 用於控制每個數據成員的精確位置。利用 Explicit, 每個成員必須使用 FieldOffsetAttribute 指示此欄位在類型中的位置。如:

[StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]
public class MySystemTime 
{
[FieldOffset(0)]public ushort wYear; 
[FieldOffset(2)]public ushort wMonth;
[FieldOffset(4)]public ushort wDayOfWeek; 
[FieldOffset(6)]public ushort wDay; 
[FieldOffset(8)]public ushort wHour; 
[FieldOffset(10)]public ushort wMinute; 
[FieldOffset(12)]public ushort wSecond; 
[FieldOffset(14)]public ushort wMilliseconds; 
}

  下麵是針對API中OSVERSIONINFO結構,在.net中定義對應類或結構的例子:

 

/**********************************************
* API中定義原結構聲明
* OSVERSIONINFOA STRUCT

* dwOSVersionInfoSize DWORD ?
* dwMajorVersion DWORD ?
* dwMinorVersion DWORD ?
* dwBuildNumber DWORD ?
* dwPlatformId DWORD ?
* szCSDVersion BYTE 128 dup (?)
* OSVERSIONINFOA ENDS
*
* OSVERSIONINFO equ <OSVERSIONINFOA>
*********************************************/

//.net中聲明為類
[ StructLayout( LayoutKind.Sequential )] 
public class OSVersionInfo 
{ 
public int OSVersionInfoSize;
public int majorVersion; 
public int minorVersion;
public int buildNumber;
public int platformId;

[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=128 )] 
public String versionString;
}
[ StructLayout( LayoutKind.Sequential )] 
public struct OSVersionInfo2 
{
public int OSVersionInfoSize;
public int majorVersion; 
public int minorVersion;
public int buildNumber;
public int platformId;

[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=128 )] 
public String versionString;
}

 

  此例中用到MashalAs特性,它用於描述欄位、方法或參數的封送處理格式。用它作為參數首碼並指定目標需要的數據類型。例如,以下代碼將兩個參數作為數據類型長指針封送給 Windows API 函數的字元串 (LPStr):

[MarshalAs(UnmanagedType.LPStr)]
string existingfile;
[MarshalAs(UnmanagedType.LPStr)]
string newfile;

 

  註意結構作為參數時候,一般前面要加上ref修飾符,否則會出現錯誤:對象的引用沒有指定對象的實例。

 

[ DllImport( "kernel32", EntryPoint="GetVersionEx" )] 
public static extern bool GetVersionEx2( ref OSVersionInfo2 osvi );

 

  三、如何保證使用托管對象的平臺調用成功?

  如果在調用平臺 invoke 後的任何位置都未引用托管對象,則垃圾回收器可能將完成該托管對象。這將釋放資源並使句柄無效,從而導致平臺invoke 調用失敗。用 HandleRef 包裝句柄可保證在平臺 invoke 調用完成前,不對托管對象進行垃圾回收。

  例如下麵:

 

FileStream fs = new FileStream( "a.txt", FileMode.Open );
StringBuilder buffer = new StringBuilder( 5 );
int read = 0;
ReadFile(fs.Handle, buffer, 5, out read, 0 ); //調用Win API中的ReadFile函數

 

  由於fs是托管對象,所以有可能在平臺調用還未完成時候被垃圾回收站回收。將文件流的句柄用HandleRef包裝後,就能避免被垃圾站回收:

 

[ DllImport( "Kernel32.dll" )]
public static extern bool ReadFile( 
HandleRef hndRef, 
StringBuilder buffer, 
int numberOfBytesToRead, 
out int numberOfBytesRead, 
ref Overlapped flag );


FileStream fs = new FileStream( "HandleRef.txt", FileMode.Open );
HandleRef hr = new HandleRef( fs, fs.Handle );
StringBuilder buffer = new StringBuilder( 5 );
int read = 0;
// platform invoke will hold reference to HandleRef until call ends
ReadFile( hr, buffer, 5, out read, 0 );

示例:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ApplicationTest
{
    public class ApplicationShow
    {
        [DllImport("user32.dll")]
        private extern static int GetWindow(int hWnd, int wCmd);

        [DllImport("user32.dll")]
        private extern static int GetWindowLongA(int hWnd, int Indx);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private extern static int GetWindowTextLength(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern bool GetWindowText(int hWnd, StringBuilder title, int maxBufSize);

        private const int GW_HWNDFIRST = 0;
        private const int GW_HWNDNEXT = 2;
        private const int GWL_STYLE = (-16);
        private const int WS_VISIBLE = 268435456;
        private const int WS_BORDER = 8388608;

        public static List<string> GetRunApplication(Form form)
        {
            List<string> appString = new List<string>();

            try
            {
                int handle = (int)form.Handle;
                int hwCurr;
                hwCurr = GetWindow(handle, GW_HWNDFIRST);
                while (hwCurr > 0)
                {
                    int isTask = (WS_VISIBLE | WS_BORDER);
                    int lngStyle = GetWindowLongA(hwCurr, GWL_STYLE);
                    bool taskWindow = ((lngStyle & isTask) == isTask);
                    if (taskWindow)
                    {
                        int length = GetWindowTextLength(new IntPtr(hwCurr));
                        StringBuilder sb = new StringBuilder(2 * length + 1);
                        GetWindowText(hwCurr, sb, sb.Capacity);
                        string strTitle = sb.ToString();
                        if (!string.IsNullOrEmpty(strTitle))
                        {
                            appString.Add(strTitle);
                        }
                    }
                    hwCurr = GetWindow(hwCurr, GW_HWNDNEXT);
                }
                return appString;
            }
            catch (Exception ex)
            {

                throw;
            }

        }
    }
}

測試

        private void btn_test_Click(object sender, EventArgs e)
        {
            List<string> list = ApplicationShow.GetRunApplication(this);
            list.ForEach(process =>
            {
                lbox_test.Items.Add(process);
            });

        }

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.面向對象 面向對象的特點:封裝、繼承、多態、抽象 封裝:封裝是把過程和數據包圍起來,對數據的訪問只能通過已定義的介面。封裝是一種信息隱藏技術,在java中通過關鍵字private,protected和public實現封裝。 適當的封裝可以讓程式碼更容易理解和維護,也加強了程式碼的安全性。 繼承: ...
  • 1.abstract(抽象) 抽象含義:具有某種對象的特征,但不完整。(似是而非) 1.1 抽象類 語法: 在class關鍵字前面,加上abstract,代表這個類是一個抽象類 public abstract class Test{ ....... } 作用: 可被子類繼承,提供共性屬性和方法 可聲 ...
  • 關註公眾號:CoderBuff,回覆“redis”獲取《Redis5.x入門教程》完整版PDF。 《Redis5.x入門教程》目錄 "第一章 · 準備工作" "第二章 · 數據類型" "第三章 · ​命令" "第四章 ​· 配置" "第五章 · Java客戶端(上)" "第六章 · 事務" "第七章 ...
  • 在許多用戶框架中(特別是WPF之前的框架,如Windows窗體和MFC),開發人員必須從頭構建自己的動畫系統。最常用的技術是結合使用計時器和一些自定義的繪圖邏輯。WPF通過自帶的基於屬性的動畫系統,改變了這種情況。接下來的兩節將描述這兩者之間的區別。 一、基於時間的動畫 假如需要旋轉Windows窗 ...
  • 面向對象程式設計(Object-Oriented Programming,OOP)是一種程式設計架構,同時也是一種程式開發的方法。對象指的是類的實例,它將對象作為程式的基本單元,將程式和數據封裝其中,以提高代碼的重用性、靈活性和擴展性。 1.1 面向對象概念 時間是由什麼組成的?現實世界是由一個一個 ...
  • public static int PostFile(string getUrl, CookieContainer cookieContainer, HttpHeader header, string postdata, byte[] postdatabyte, Stream sm) { Strea ...
  • 現在在項目里大多都是直接使用微軟的依賴註入框架,而微軟的註入方式比較簡單,不如 AutoFac 使用起來靈活,於是想給微軟的依賴註入增加一些擴展,使得可以像AutoFac 一樣比較靈活的註冊服務 ...
  • 使用Aspose組件導出 Aspose有Aspose.Slides.dll,可以無需安裝office,進行讀寫PPT文件。 Aspose可能通過Aspose.Slides.NET安裝 簡單的導出圖片demo,如下: 1 internal class PptToImagesConverter 2 { ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...