DllImport进阶:参数配置与高级主题探究
深入讨论DllImport属性的作用和配置方法
在基础篇中,我们已经简单介绍了DllImport的一些属性。现在我们将深入探讨这些属性的实际应用。
1. EntryPoint

EntryPoint属性用于指定要调用的非托管函数的名称。如果托管代码中的函数名与非托管代码中的函数名不同,可以使用这个属性。例如:
[DllImport("user32.dll", EntryPoint = "MessageBoxW")]
public static extern int ShowMessage(IntPtr hWnd, String text, String caption, uint type);
在这个例子中,我们将非托管函数MessageBoxW映射到托管函数ShowMessage。
2. CallingConvention
CallingConvention属性指定调用约定,它定义了函数如何接收参数和返回值。常见的调用约定包括:
- CallingConvention.Cdecl:调用者清理堆栈,多用于C/C++库。
- CallingConvention.StdCall:被调用者清理堆栈,Windows API常用。
- CallingConvention.ThisCall:用于C++类方法。
- CallingConvention.FastCall:用于快速调用,较少使用。
示例:
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern bool Beep(uint dwFreq, uint dwDuration);
3. CharSet
CharSet属性用于指定字符串的字符集,影响字符串的处理和传递方式。主要选项有:
- CharSet.Ansi:将字符串作为ANSI编码传递。
- CharSet.Unicode:将字符串作为Unicode编码传递。
- CharSet.Auto:根据平台自动选择ANSI或Unicode。
示例:
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);
4. SetLastError
SetLastError属性指定是否在调用非托管函数后调用GetLastError。设置为true时,可以使用Marshal.GetLastWin32Error获取错误代码。
示例:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);public void CloseResource(IntPtr handle)
{if (!CloseHandle(handle)){int error = Marshal.GetLastWin32Error();// 处理错误}
}
5. ExactSpelling
ExactSpelling属性指定是否精确匹配入口点名称。默认情况下,CharSet影响名称查找,设置为true时,关闭字符集查找。
示例:
[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);
6. PreserveSig
PreserveSig属性指定是否保留方法签名的HRESULT返回类型。默认值为true。当设置为false时,HRESULT会转换为异常。
示例:
[DllImport("ole32.dll", PreserveSig = false)]
public static extern void CoCreateGuid(out Guid guid);
7. BestFitMapping 和 ThrowOnUnmappableChar
BestFitMapping属性控制是否启用ANSI到Unicode的最佳映射。ThrowOnUnmappableChar指定是否在遇到无法映射的字符时抛出异常。
示例:
[DllImport("kernel32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern bool SetEnvironmentVariable(string lpName, string lpValue);
实践示例
下面是一个综合使用多个DllImport属性的示例:
using System;
using System.Runtime.InteropServices;class Program
{[DllImport("user32.dll", EntryPoint = "MessageBox", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)]public static extern int ShowMessageBox(IntPtr hWnd, String text, String caption, uint type);static void Main(){int result = ShowMessageBox(IntPtr.Zero, "Hello, World!", "Hello Dialog", 0);if (result == 0){int error = Marshal.GetLastWin32Error();Console.WriteLine($"Error: {error}");}}
}
在这个例子中,我们使用了EntryPoint、CharSet、SetLastError和CallingConvention属性来精确配置MessageBox函数的调用。

深入理解和正确配置DllImport属性可以帮助我们更高效地调用非托管代码,确保数据类型和调用约定的匹配,处理潜在的错误和异常,提升代码的稳定性和安全性。
探讨数据类型匹配的重要性
在C#中通过DllImport调用非托管代码时,数据类型的匹配是确保代码正确执行的关键因素之一。正确的数据类型匹配能够避免数据损坏、内存泄漏和程序崩溃等问题。
1. 数据类型匹配的重要性
- 避免数据损坏:非托管代码和托管代码的数据类型必须一致,否则传递的数据可能会损坏。例如,将一个32位的整数传递给一个预期为64位整数的非托管函数会导致数据截断或损坏。
- 防止程序崩溃:不匹配的数据类型可能会导致非托管代码访问非法内存地址,进而导致程序崩溃。
- 确保数据完整性:正确的数据类型匹配可以确保数据在托管代码和非托管代码之间正确传递,保持数据的完整性。
- 提高代码安全性:数据类型的不匹配可能会引入安全漏洞,导致潜在的缓冲区溢出等安全问题。
2. 基本数据类型的匹配
基本数据类型在托管代码和非托管代码之间的匹配非常重要。以下是常见数据类型的匹配示例:
- 整数类型
- C#中的int通常对应C/C++中的int或LONG类型:
[DllImport("Example.dll")]
public static extern int Add(int a, int b);
- 无符号整数类型
- C#中的uint通常对应C/C++中的unsigned int或DWORD类型:
[DllImport("Example.dll")]
public static extern uint GetTickCount();
- 长整数类型
- C#中的long对应C中的long long或__int64类型:
[DllImport("Example.dll")]
public static extern long Multiply(long a, long b);
- 指针类型
- C#中的IntPtr或UIntPtr对应C中的指针类型,如void*或HANDLE:
[DllImport("Example.dll")]
public static extern IntPtr OpenHandle(uint access);
- 布尔类型
- C#中的bool对应C中的BOOL类型,需要注意的是,C/C++中的BOOL通常定义为int,而C#中的bool是1字节。
[DllImport("Example.dll")] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr handle);
3. 复杂数据类型的匹配
对于结构体、数组和字符串等复杂数据类型的匹配,需要特别注意。
- 结构体
- 结构体需要在托管代码和非托管代码中保持一致,并使用StructLayout属性进行布局控制:
[StructLayout(LayoutKind.Sequential)]
public struct Point { public int X; public int Y; } [DllImport("Example.dll")]
public static extern void GetPoint(out Point p);
- 数组
- 数组的匹配需要使用MarshalAs属性指定数组的类型和大小:
[DllImport("Example.dll")]
public static extern void FillArray([MarshalAs(UnmanagedType.LPArray, SizeConst = 10)] int[] array);
- 字符串
- 字符串的匹配需要注意字符集的选择(如CharSet.Ansi或CharSet.Unicode):
[DllImport("Example.dll", CharSet = CharSet.Unicode)]
public static extern void PrintMessage(string message);
4. 数据类型匹配的常见问题及解决方法
- 字符集不匹配:在传递字符串时,如果字符集不匹配,可能会导致字符串被截断或乱码。解决方法是在DllImport特性中明确指定字符集:
[DllImport("Example.dll", CharSet = CharSet.Unicode)]
public static extern void PrintMessage(string message);
- 指针类型不匹配:非托管代码中的指针类型应对应C#中的IntPtr或UIntPtr:
[DllImport("Example.dll")]
public static extern IntPtr AllocateMemory(uint size);
- 结构体布局不匹配:如果结构体在内存中的布局不同,可能会导致数据损坏。解决方法是使用StructLayout属性确保一致的内存布局:
[StructLayout(LayoutKind.Sequential)]
public struct Point { public int X; public int Y; }
- 数组边界问题:传递数组时,应确保数组的大小匹配,避免越界访问:
[DllImport("Example.dll")]
public static extern void ProcessArray([MarshalAs(UnmanagedType.LPArray, SizeConst = 10)] int[] array);
讨论内存管理的重要性
在调用非托管代码时,内存管理是一个不可忽视的重要环节。非托管代码不受.NET垃圾回收器的管理,因此需要开发人员手动分配和释放内存。这不仅涉及到如何正确使用内存,还包括如何避免内存泄漏和其他潜在问题。
1. 内存管理的重要性
- 防止内存泄漏:手动分配的内存如果不正确释放,会导致内存泄漏,逐渐消耗系统资源。
- 确保数据安全:未正确管理的内存可能会被覆盖或误用,导致数据损坏和程序崩溃。
- 提高程序性能:高效的内存管理能够减少内存使用,提升程序性能。
2. 内存分配和释放
在非托管代码中,内存通常使用malloc、calloc等函数分配,并使用free函数释放。在托管代码中,我们可以使用Marshal类提供的方法来分配和释放非托管内存。
- 分配内存Marshal.AllocHGlobal:分配指定字节数的非托管内存。Marshal.AllocCoTaskMem:分配任务内存,适用于COM互操作。
IntPtr ptr = Marshal.AllocHGlobal(100); // 分配100字节的内存
// 使用ptr进行操作
Marshal.FreeHGlobal(ptr); // 释放内存
- 释放内存使用Marshal.FreeHGlobal或Marshal.FreeCoTaskMem释放之前分配的内存。
IntPtr ptr = Marshal.AllocCoTaskMem(100);
// 使用ptr进行操作
Marshal.FreeCoTaskMem(ptr); // 释放内存
3. 内存拷贝
在托管代码和非托管代码之间传递数据时,可能需要进行内存拷贝。Marshal类提供了一些方法用于内存拷贝:
- Marshal.Copy:用于从托管数组复制到非托管内存,或从非托管内存复制到托管数组。
- Marshal.StructureToPtr:将托管结构复制到非托管内存。
- Marshal.PtrToStructure:将非托管内存的数据复制到托管结构。
int[] managedArray = new int[10];
IntPtr unmanagedArray = Marshal.AllocHGlobal(managedArray.Length * sizeof(int));Marshal.Copy(managedArray, 0, unmanagedArray, managedArray.Length);
// 使用unmanagedArray进行操作
Marshal.Copy(unmanagedArray, managedArray, 0, managedArray.Length);Marshal.FreeHGlobal(unmanagedArray);
4. 处理非托管资源
调用非托管代码时,可能会使用非托管资源(如文件句柄、窗口句柄等),这些资源也需要正确管理以避免资源泄漏。
- 关闭句柄使用CloseHandle或类似的API来关闭非托管资源。
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);public void CloseResource(IntPtr handle)
{if (!CloseHandle(handle)){int error = Marshal.GetLastWin32Error();// 处理错误}
}
5. 管理生命周期
对于需要频繁分配和释放内存的操作,可以考虑封装内存管理逻辑,确保内存能够正确释放。
public class UnmanagedBuffer : IDisposable
{private IntPtr buffer;private bool disposed = false;public UnmanagedBuffer(int size){buffer = Marshal.AllocHGlobal(size);}~UnmanagedBuffer(){Dispose(false);}public void Dispose(){Dispose(true);GC.SuppressFinalize(this);}protected virtual void Dispose(bool disposing){if (!disposed){if (buffer != IntPtr.Zero){Marshal.FreeHGlobal(buffer);buffer = IntPtr.Zero;}disposed = true;}}public IntPtr Buffer => buffer;
}
6. 内存管理最佳实践
- 始终释放内存:确保所有分配的内存都在适当的时候释放,防止内存泄漏。
- 使用智能指针或封装类:封装内存管理逻辑,减少手动管理的复杂性。
- 定期检查内存使用:使用工具和代码分析,确保没有未释放的内存。
实践示例
以下是一个综合示例,展示了内存分配、内存拷贝和资源管理的完整流程:

C++部分代码:PointManager.h和PointManager.cpp两个文件
#pragma once#ifdef EXAMPLE_EXPORTS
#define EXAMPLE_API __declspec(dllexport)
#else
#define EXAMPLE_API __declspec(dllimport)
#endifstruct Point
{int X;int Y;
};extern "C" EXAMPLE_API Point* CreatePoint(int x, int y);
extern "C" EXAMPLE_API void GetPoint(Point * point, Point * pOut);
extern "C" EXAMPLE_API void DeletePoint(Point * point);
#include "pch.h"
#include "PointManager.h"// 创建一个新的 Point 对象并返回其指针
extern "C" __declspec(dllexport) Point* CreatePoint(int x, int y)
{Point* p = new Point();p->X = x;p->Y = y;return p;
}// 获取 Point 对象的值
extern "C" __declspec(dllexport) void GetPoint(Point * point, Point * pOut)
{if (point == nullptr || pOut == nullptr){SetLastError(ERROR_INVALID_PARAMETER);return;}pOut->X = point->X;pOut->Y = point->Y;
}// 删除 Point 对象
extern "C" __declspec(dllexport) void DeletePoint(Point * point)
{if (point != nullptr){delete point;}
}
C#部分代码:
using System;
using System.Runtime.InteropServices;class Program
{[StructLayout(LayoutKind.Sequential)]public struct Point{public int X;public int Y;}[DllImport("Example.dll", SetLastError = true)]public static extern IntPtr CreatePoint(int x, int y);[DllImport("Example.dll", SetLastError = true)]public static extern void GetPoint(IntPtr point, out Point p);[DllImport("Example.dll", SetLastError = true)]public static extern void DeletePoint(IntPtr point);static void Main(){IntPtr pointPtr = CreatePoint(10, 20);if (pointPtr == IntPtr.Zero){int error = Marshal.GetLastWin32Error();Console.WriteLine($"Error: {error}");return;}Point p;GetPoint(pointPtr, out p);Console.WriteLine($"Point: {p.X}, {p.Y}");DeletePoint(pointPtr);}
}
这个示例展示了如何在非托管代码中创建和管理内存资源,并在托管代码中正确分配和释放内存。

参考文档
使用非托管 DLL 函数 - .NET Framework | Microsoft Learn
标识 DLL 中的函数 - .NET Framework | Microsoft Learn
DllImportAttribute.EntryPoint 字段 (System.Runtime.InteropServices) | Microsoft Learn
原文链接:https://www.toutiao.com/article/7383720159233573427/?app=news_article×tamp=1719443240&use_new_style=1&req_id=2024062707072029288E0C168B30524880&group_id=7383720159233573427&share_token=27F51CAD-7939-4769-90A3-0F26B5F997C4&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_ios&utm_campaign=client_share&wxshare_count=1&source=m_redirect&wid=1719451374672
相关文章:
DllImport进阶:参数配置与高级主题探究
深入讨论DllImport属性的作用和配置方法 在基础篇中,我们已经简单介绍了DllImport的一些属性。现在我们将深入探讨这些属性的实际应用。 1. EntryPoint EntryPoint属性用于指定要调用的非托管函数的名称。如果托管代码中的函数名与非托管代码中的函数名不同&#…...
HTTP与HTTPS协议区别及应用场景
HTTP(超文本传输协议)和 HTTPS(安全超文本传输协议)都是用于通过网络传输数据的协议。虽然它们有一些相似之处,但在安全性和数据保护方面也存在显著差异。 在这篇博文中,我们将探讨 HTTP 和 HTTPS…...
Vue2-Vue Router前端路由实现思路
1.路由是什么? Router路由器:数据包转发设备,路由器通过转发数据包(数据分组)来实现网络互连 Route路由:数据分组从源到目的地时,决定端到端路径的网络范围的进程 | - 网络层 Distribute分发…...
2024 年 亚太赛 APMCM (C题)中文赛道国际大学生数学建模挑战赛 | 量子计算的物流配送 | 数学建模完整代码+建模过程全解全析
当大家面临着复杂的数学建模问题时,你是否曾经感到茫然无措?作为2022年美国大学生数学建模比赛的O奖得主,我为大家提供了一套优秀的解题思路,让你轻松应对各种难题! 完整内容可以在文章末尾领取! 该段文字…...
客观分析-自己和本科学生之间的差距
进入专科学校和与985、211等重点本科院校学生之间的差距可能由多种因素造成,这些因素可能包括但不限于: 1. **入学标准**: 985和211工程院校通常有更高的入学标准和更严格的选拔过程。 你得使你自己适应更高的入学标准和更严格的选拔过程&am…...
清华镜像源
python在安装各种库的时候为了下载速度快,经常使用镜像源,下面是使用清华镜像源案例。其中的 xxx 表示要安装的库,如 requests。 pip install xxx -i https://pypi.tuna.tsinghua.edu.cn/simple 安装requests案例:pip install r…...
大语言模型测评工具-ChatHub和ChatAll
背景 现在国内外拥有上百个大语言模型,在AI业务中,我们需要在其中选择一个合适业务模型,就需要对这些模型进行测试。手工去测试这么多模型效率一定不高,今天就介绍两个提高测评模型效率的工具 ChatHub和ChatAll。 介绍 ChatHub…...
使用redis分布式锁,不要把锁放在本地事务内部
在使用分布式锁的时候,习惯性的尽量缩小同步代码块的范围。 但是如果数据库隔离级别是可重复读,这种情况下不要把分布式锁加在Transactional注解的事务方法内部。 因为可能会出现这种情况: 线程1开启事务A后获取分布式锁,执行业务代码后在事务内释放了分布式锁。…...
Python学生信息管理系统(完整代码)
引言:(假装不是一个大学生课设)在现代教育管理中,学生管理系统显得尤为重要。这种系统能够帮助教育机构有效地管理学生资料、成绩、出勤以及其他教育相关活动,从而提高管理效率并减少人为错误。通过使用Python…...
【大功率汽车大灯升压方案】LED恒流驱动芯片FP7208升压车灯调光应用,PWM内部转模拟,调光深度1%,无频闪顾虑,低亮无抖动
文章目录 前言 一、LED车灯的内部组成结构 二、驱动板详解 三、FP7208芯片介绍 芯片参数 总结 前言 近年来,汽车市场飞速发展,车灯作为汽车重要的组成部分,也得到了广泛的关注。车灯对于汽车不仅是外观件更是汽车主动安全的重要组成部…...
uniapp应用如何实现传感器数据采集和分析
UniApp是一种跨平台的应用开发框架,它支持在同一份代码中同时开发iOS、Android、H5等多个平台的应用。在UniApp中实现传感器数据采集和分析的过程可以分为以下几个步骤: 引入相关插件或库 UniApp通过插件或库的形式扩展功能。对于传感器数据采集和分析&…...
读书笔记-Java并发编程的艺术-第3章(Java内存模型)-第6节(final域的内存语义)
文章目录 3.6 final域的内存语义3.6.1 final 域的重排序规则3.6.2 写final 域的重排序规则3.6.3 读final 域的重排序规则3.6.4 final 域为引用类型3.6.5 为什么 final 引用不能从构造函数内“逸出”3.6.6 final 语义在处理器中的实现3.6.7 JSR-133 为什么要增强final 的语义 3.…...
Spring AI 1.0.0 新变化,从 0.8.1 如何升级
Spring AI 1.0.0-M1 版本已经发布,距离 1.0.0 正式版又更近了一步。同时这也意味着,Spring AI 1.0.0 的 API 已经基本确定,不会发生大的改动。这里介绍一下,相对于上一个发布版本 0.8.1,Spring AI 1.0.0 的一些重要的变…...
【机器学习】FFmpeg+Whisper:二阶段法视频理解(video-to-text)大模型实战
目录 一、引言 二、FFmpeg工具介绍 2.1 什么是FFmpeg 2.2 FFmpeg核心原理 2.3 FFmpeg使用示例 三、FFmpegWhisper二阶段法视频理解实战 3.1 FFmpeg安装 3.2 Whisper模型下载 3.3 FFmpeg抽取视频的音频 3.3.1 方案一:命令行方式使用ffmpeg 3.3.2 方案二&a…...
Java中继承接口和实现接口的区别、接口和抽象类的区别、并理解关键字interface、implements
初学者容易把继承接口和实现接口搞混,专门整理一下,顺便简单介绍一下interface、implements关键字。 继承接口和实现接口的区别、接口的特点 继承接口是说的只有接口才可以继承接口,是接口与接口间的。实现接口是说的接口与类之间ÿ…...
Excel为数据绘制拆线图,并将均值线叠加在图上,以及整个过程的区域录屏python脚本
Excel为数据绘制拆线图,并将均值线叠加在图上,以及整个过程的区域录屏python脚本 1.演示动画A.视频B.gif动画 2.跟踪鼠标区域的录屏脚本 Excel中有一组数据,希望画出曲线,并且能把均值线也绘制在图上,以下动画演示了整个过程,并且提供了区域录屏脚本,原理如下: 为节约空间,避免…...
易保全推动区块链应用与AI融合创新发展
数字化时代,区块链和人工智能技术作为当下两大“黑科技”,两者的深度结合,正在为企业数字化转型带来前所未有的机遇。 易保全作为国内权威的电子数据存证保全机构,积极探索两者的融合之道,将区块链的去中心化、不可篡…...
C++(Python)肥皂泡沫普拉托边界膜曲面模型算法
🎯要点 🎯肥皂泡二维流体模拟 | 🎯泡沫普拉托边界膜曲面模型算法演化厚度变化 | 🎯螺旋曲面三周期最小结构生成 📜皂膜用例:Python计算物理粒子及拉格朗日和哈密顿动力学 | Python和MATLAB粘性力接触力动…...
VBA打开其他Excel文件
前言 本节会介绍通过VBA实现打开其他excel文件,包括模糊匹配文件名称、循环同时打开多个文件,并获取工作表及工作簿进行数据操作后,对打开的文件进行保存并关闭操作。 一、打开固定文件名称的文件 场景说明: 1.新建一个宏文件VBA…...
模拟 ADC 的前端
ADC 的 SPICE 模拟 反复试验的方法将信号发送到 ADC 非常耗时,而且可能有效也可能无效。如果转换器捕获电压信息的关键时刻模拟输入引脚不稳定,则无法获得正确的输出数据。SPICE 模型允许您执行的步是验证所有模拟输入是否稳定,以便没有错误…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
