Windows COM技术:COM介绍、代码演示。

目录

步骤一:理解COM技术

介绍COM的基础知识

1. COM的目的和特点

2. COM的关键概念

3. COM的实现

4. COM与DCOM、ActiveX

讨论COM的用途

1. 软件自动化

2. 插件和扩展

3. 跨语言开发

4. 分布式计算

5. 系统级组件

6. 网络浏览器插件

步骤二:设置开发环境

步骤三:编写COM组件

1. 定义COM接口

定义接口和CLSID

2. 实现接口

3. 注册组件

步骤四:使用COM组件

自动调用,需注册DLL

手动调用

步骤五:单元测试

单元测试策略

步骤六:更新DLL并兼容老的

1. 使用版本控制

2. 使用COM的接口继承

示例:加入新功能到COM DLL

出现的问题及其成因


步骤一:理解COM技术

介绍COM的基础知识

        件对象模型(COM)是一个由微软开发的软件架构,旨在促进不同软件组件之间的二进制交互。COM定义了一种方法,使得在各种编程语言中编写的组件可以相互通信,不仅在同一个程序内部,而且可以在不同的计算机上。以下是对COM基础知识的更详尽的解释:

1. COM的目的和特点

        COM是为了解决软件开发中的可重用性、灵活性和版本控制等问题而设计的。其主要特点包括:

  • 语言中立性:COM允许用任何支持COM的编程语言(如C++, Visual Basic, Delphi等)编写的组件彼此交互。
  • 二进制互操作性:COM组件以二进制标准进行通信,这意味着可以在不重新编译调用者代码的情况下替换组件。
  • 位置透明性:COM组件可以在本地机器上运行,也可以通过网络在远程机器上运行。

2. COM的关键概念

        理解COM的工作原理涉及几个核心概念:

  • 接口:COM使用接口与实现分离的方式,组件提供的功能通过一组严格定义的接口暴露。接口是一组函数的集合,类似于C++中的纯虚函数的类定义。
  • GUID和IID:每个COM接口和组件都有一个全局唯一标识符(GUID),接口的GUID也称为接口ID(IID)。这些标识符确保COM的注册和引用是唯一的。
  • COM库和注册表:COM组件在系统中的注册依靠COM库来管理,每个组件的信息(包括位置和可用的接口)都记录在Windows注册表中。
  • 引用计数:COM使用引用计数来管理内存和生命周期,组件负责跟踪有多少客户端正在使用它,并相应地管理其生命周期。

3. COM的实现

        在实际应用中,一个COM对象通常会通过以下步骤实现和使用:

  • 创建实例:客户端通过CoCreateInstance()等函数创建COM对象实例。
  • 接口查询:使用IUnknown接口的QueryInterface方法来查询支持的接口。IUnknown是所有COM接口的基接口,提供了对象生命周期管理和接口查询的基本方法。
  • 调用接口:一旦获取了接口的指针,客户端就可以调用其方法来执行操作。
  • 释放接口:完成操作后,客户端需要调用接口的Release方法来减少其引用计数,当引用计数达到0时,COM对象将自行销毁。

4. COM与DCOM、ActiveX

  • DCOM:分布式组件对象模型(DCOM)是COM的扩展,支持在网络上的不同计算机之间通信。
  • ActiveX:ActiveX控件是基于COM的一种特殊形式,用于在网页上嵌入和执行特定功能的组件。

        COM为开发者提供了一种强大的机制,用于创建可在多个程序之间共享的模块化组件。尽管现代软件开发中已经出现了许多新的技术和方法,但在需要高度稳定和兼容性的大型企业应用中,COM依然具有其独特的价值。通过确保组件可以被不同语言编写的应用程序使用,并且可以被安全地更新和替换,COM帮助软件系统实现了更好的维护性和

讨论COM的用途

        组件对象模型(COM)是一个为了提高软件模块化和可重用性而设计的技术。自从1990年代初期由微软引入以来,它已经在各种应用中证明了其价值。COM的设计允许开发者创建灵活、可重用的组件,这些组件可以被不同的应用程序在不同的环境中使用,而不需要了解组件的内部实现细节。以下是一些具体的COM用途:

1. 软件自动化

        COM广泛用于办公软件的自动化。通过COM接口,应用程序(如Microsoft Office套件)可以暴露其功能给外部脚本或程序,允许自动化复杂的任务。例如,一个企业可能使用VBA(Visual Basic for Applications)脚本与Excel交互,自动化报表的生成和数据分析。

2. 插件和扩展

        COM使得软件开发者能够为他们的应用程序创建插件架构,其他开发者可以为这些应用程序开发添加功能的插件。这种插件通常以DLL的形式实现,并通过COM接口与主应用程序通信。例如,图形处理软件如Photoshop可以通过COM插件来扩展其图像处理能力。

3. 跨语言开发

        由于COM的语言无关性,使用不同编程语言开发的组件可以互相操作。这意味着一个用C++编写的组件可以被一个用Visual Basic或C#开发的应用程序使用,反之亦然。这种特性极大地增加了不同软件项目间代码的可重用性。

4. 分布式计算

        通过DCOM(分布式组件对象模型),COM技术扩展到网络。DCOM允许在不同计算机上运行的组件通过网络通信,支持构建分布式应用。这在处理大型企业级应用中尤其有用,例如金融服务领域的数据处理和实时交易系统。

5. 系统级组件

        COM还用于操作系统级别的功能扩展,如Shell扩展处理器、服务组件等。Windows操作系统本身就广泛使用COM为开发者提供可扩展的API,使得第三方开发者可以创建与Windows深度集成的软件解决方案。

6. 网络浏览器插件

ActiveX控件,一种基于COM的技术,曾经是实现浏览器功能扩展的主要方式,允许网页通过嵌入的ActiveX组件来提供富交互性应用,虽然现在由于安全考虑,其使用已经大幅减少。

        COM技术的设计初衷是促进软件组件的重用,降低开发成本,并加速开发过程。虽然现代软件开发中已经有了更现代的技术(如.NET Framework),但COM因其强大的跨语言和跨平台的互操作性,在很多现存的系统中仍然保持着其重要性。通过灵活使用COM,软件开发者可以创造出更加模块化、易于管理和维护的应用程序。

步骤二:设置开发环境

安装必要的工具:需要Visual Studio和Windows SDK。

Microsoft Visual Studio C++2017+Windows 11 SDK环境_microsoft visual c++ 2017-CSDN博客

配置项目:创建一个新的Win32项目,并设置为DLL类型,因为大多数COM组件都是以DLL形式发布。

Visual Studio 2022如何创建Win32项目_vs2022怎么创建win32项目-CSDN博客

步骤三:编写COM组件

1. 定义COM接口

定义接口和CLSID

        MathOperations.h

#include <windows.h>
#include <Unknwnbase.h>

// Interface ID (IID) for IMathOperations
// {12345678-1234-1234-1234-123456789012}
static const IID IID_IMathOperations = 
{ 0x12345678, 0x1234, 0x1234, { 0x12, 0x34, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12 } };

// Class ID (CLSID) for MathOperations
// {87654321-4321-4321-4321-210987654321}
static const CLSID CLSID_MathOperations = 
{ 0x87654321, 0x4321, 0x4321, { 0x43, 0x21, 0x21, 0x09, 0x87, 0x65, 0x43, 0x21 } };

// Define the IMathOperations interface
class IMathOperations : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE Add(int a, int b, int* result) = 0;
    virtual HRESULT STDMETHODCALLTYPE Subtract(int a, int b, int* result) = 0;
};

2. 实现接口

        实现COM接口和类,并提供必要的方法和引用计数逻辑。

        MathOperations.cpp

#include "ComTest.h"

class MathOperations : public IMathOperations
{
    volatile long refCount;
public:
    MathOperations() : refCount(1) {}

    // IUnknown methods
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override {
        if (riid == IID_IUnknown || riid == IID_IMathOperations) {
            *ppvObject = static_cast<IMathOperations*>(this);
            this->AddRef();
            return S_OK;
        }
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }

    ULONG STDMETHODCALLTYPE AddRef() override {
        return InterlockedIncrement(&refCount);
    }

    ULONG STDMETHODCALLTYPE Release() override {
        ULONG res = InterlockedDecrement(&refCount);
        if (res == 0) delete this;
        return res;
    }

    // IMathOperations methods
    HRESULT STDMETHODCALLTYPE Add(int a, int b, int* result) override {
        *result = a + b;
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE Subtract(int a, int b, int* result) override {
        *result = a - b;
        return S_OK;
    }
};

extern "C" __declspec(dllexport) HRESULT CreateInstance(REFIID riid, void** ppv) {
    MathOperations* pMath = new MathOperations();
    HRESULT hr = pMath->QueryInterface(riid, ppv);
    pMath->Release();  // release initial reference
    return hr;
}

3. 注册组件

        注册COM组件通常涉及向Windows注册表添加条目,以便系统可以找到并实例化COM对象。这可以通过手动添加注册表键值或使用注册表函数在安装过程中自动完成。

        也可以直接调用dll,无需注册。

步骤四:使用COM组件

        为了调用上文定义的 COM DLL,我们需要编写一个客户端程序,该程序使用 COM 组件进行通信。以下是如何在 C++ 中编写调用 MathOperations COM 组件的代码。此代码演示了如何初始化 COM,创建组件实例,调用接口方法,以及最后如何清理。

自动调用,需注册DLL

#include <iostream>
#include <windows.h>
#include "MathOperations.h"  // 包含COM接口定义

int main() {
    HRESULT hr = CoInitialize(NULL);  // 初始化COM库
    if (FAILED(hr)) {
        std::cout << "Failed to initialize COM library." << std::endl;
        return -1;
    }

    IMathOperations* pMathOps = nullptr;  // 指向接口的指针

    // 创建组件实例
    hr = CoCreateInstance(CLSID_MathOperations,   // 组件的CLSID
                          NULL,                   // 没有外部聚合
                          CLSCTX_INPROC_SERVER,   // DLL运行在相同的进程
                          IID_IMathOperations,    // 请求的接口IID
                          (void**)&pMathOps);     // 指针存放位置

    if (SUCCEEDED(hr)) {
        int result = 0;
        
        // 调用Add方法
        hr = pMathOps->Add(5, 3, &result);
        if (SUCCEEDED(hr)) {
            std::cout << "Addition Result: " << result << std::endl;
        }

        // 调用Subtract方法
        hr = pMathOps->Subtract(5, 3, &result);
        if (SUCCEEDED(hr)) {
            std::cout << "Subtraction Result: " << result << std::endl;
        }

        // 释放接口
        pMathOps->Release();
    } else {
        std::cout << "Failed to create component instance." << std::endl;
    }

    CoUninitialize();  // 清理COM
    return 0;
}

关键步骤解释

  1. 初始化 COM

    • 使用 CoInitializeCoInitializeEx 初始化 COM 库,这是使用 COM 组件前必需的步骤。
  2. 创建 COM 对象实例

    • 使用 CoCreateInstance 函数来创建 COM 组件的实例。这个函数需要 CLSID 来找到正确的组件,还需要 IID 来获取指定的接口。
  3. 调用接口方法

    • 一旦获取了接口指针,就可以调用定义的方法。此示例中调用了 AddSubtract 方法。
  4. 释放接口

    • 完成操作后,需要调用接口的 Release 方法来减少引用计数。当引用计数达到零时,COM 对象会被销毁。
  5. 清理 COM

    • 在程序结束前,使用 CoUninitialize 清理 COM 环境。

        这段代码假设 COM 组件已经正确注册在系统上,且客户端和服务器共享接口定义(头文件)。这是在同一台机器上或已经通过某种方式共享了头文件的情况。在实际部署中,通常需要将接口的定义(通常是 IDL 文件或编译后的类型库)与客户端开发者共享。

手动调用

        如果您想要手动加载 DLL 而不是使用 CoCreateInstance() 来自动加载,可以采用显式加载的方式。这通常涉及使用 Windows API 如 LoadLibraryGetProcAddress 来动态加载 DLL 并获取函数指针。这种方式对于 COM 组件来说稍微复杂,但可行,尤其是在某些特定环境中,如当你没有注册 COM 组件到系统注册表时。 

#include <iostream>
#include <windows.h>
#include "MathOperations.h"  // 包含COM接口定义

// 定义函数指针类型
typedef HRESULT (*PFN_CREATE_INSTANCE)(REFIID riid, void** ppv);

int main() {
    HMODULE hDll = LoadLibrary(TEXT("MathOperations.dll"));  // 动态加载DLL
    if (hDll == NULL) {
        std::cout << "Failed to load DLL." << std::endl;
        return -1;
    }

    // 获取函数指针
    PFN_CREATE_INSTANCE pfnCreateInstance = (PFN_CREATE_INSTANCE)GetProcAddress(hDll, "CreateInstance");
    if (pfnCreateInstance == NULL) {
        std::cout << "Failed to get function address." << std::endl;
        FreeLibrary(hDll);
        return -1;
    }

    HRESULT hr = CoInitialize(NULL);  // 初始化COM库
    if (FAILED(hr)) {
        std::cout << "Failed to initialize COM library." << std::endl;
        FreeLibrary(hDll);
        return -1;
    }

    IMathOperations* pMathOps = nullptr;  // 指向接口的指针

    // 创建组件实例
    hr = pfnCreateInstance(IID_IMathOperations, (void**)&pMathOps);
    if (SUCCEEDED(hr)) {
        int result = 0;

        // 调用Add方法
        hr = pMathOps->Add(5, 3, &result);
        if (SUCCEEDED(hr)) {
            std::cout << "Addition Result: " << result << std::endl;
        }

        // 调用Subtract方法
        hr = pMathOps->Subtract(5, 3, &result);
        if (SUCCEEDED(hr)) {
            std::cout << "Subtraction Result: " << result << std::endl;
        }

        // 释放接口
        pMathOps->Release();
    } else {
        std::cout << "Failed to create component instance." << std::endl;
    }

    CoUninitialize();  // 清理COM
    FreeLibrary(hDll); // 释放DLL
    return 0;
}

关键改动说明:

  1. 加载 DLL

    • 使用 LoadLibrary 加载 DLL 文件。这需要 DLL 文件的路径,路径可以是绝对或相对的。
  2. 获取函数地址

    • 使用 GetProcAddress 获取 DLL 中导出函数的地址。这里的 CreateInstance 是假设你的 DLL 中导出了一个创建 COM 对象的函数。
  3. 手动创建实例

    • 使用获取到的函数指针 pfnCreateInstance 来创建 COM 对象的实例。
  4. 释放资源

    • 使用 FreeLibrary 释放加载的 DLL。这是确保资源正确管理的重要步骤。

步骤五:单元测试

        讨论如何为COM组件编写和执行单元测试,确保其功能正确。

单元测试策略

  1. 测试环境设置

    • 确保COM环境已正确初始化。
    • 加载COM组件,以便在测试期间使用。
  2. 测试案例设计

    • 对每个方法执行正常值测试。
    • 对每个方法进行边界值测试。
    • 对每个方法进行错误处理测试,例如输入无效参数。
  3. 资源清理

    • 测试完成后,正确释放所有资源。
#include "pch.h"
#include "CppUnitTest.h"
#include "../MathOperations/MathOperations.h"  // 包含MathOperations接口定义

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace MathOperationsTests
{
    TEST_CLASS(MathOperationsTests)
    {
    public:
        IMathOperations* pMathOps = nullptr;

        // 测试初始化
        TEST_METHOD_INITIALIZE(Setup)
        {
            CoInitialize(NULL);  // 初始化COM
            HRESULT hr = CoCreateInstance(CLSID_MathOperations, NULL, CLSCTX_INPROC_SERVER,
                                          IID_IMathOperations, (void**)&pMathOps);
            Assert::IsTrue(SUCCEEDED(hr));
        }

        // 测试清理
        TEST_METHOD_CLEANUP(Teardown)
        {
            if (pMathOps)
            {
                pMathOps->Release();
                pMathOps = nullptr;
            }
            CoUninitialize();  // 清理COM
        }

        // 测试Add方法
        TEST_METHOD(TestAdd)
        {
            int result = 0;
            HRESULT hr = pMathOps->Add(10, 20, &result);
            Assert::AreEqual(S_OK, hr);
            Assert::AreEqual(30, result);
        }

        // 测试Subtract方法
        TEST_METHOD(TestSubtract)
        {
            int result = 0;
            HRESULT hr = pMathOps->Subtract(30, 10, &result);
            Assert::AreEqual(S_OK, hr);
            Assert::AreEqual(20, result);
        }
    };
}

步骤六:更新DLL并兼容老的

1. 使用版本控制

        在DLL中,通常通过接口的版本控制来保持向后兼容性。为此,你可以:

  • 保留旧接口不变:确保原有的接口不发生变化,以保证依赖于这些接口的现有应用程序可以继续无缝工作。
  • 新增接口:为新功能创建新的接口。这可以通过继承原有接口并添加新方法来实现,或者定义一个完全独立的新接口。

2. 使用COM的接口继承

对于COM组件,接口继承是保持老版本兼容的常见方法。具体步骤如下:

  • 定义新接口:基于现有的接口(如 IMathOperations),你可以定义一个新接口(如 IMathOperations2),在其中加入新的方法。
  • 实现新接口:在COM类中实现这个新接口,同时保留对旧接口的支持。

示例:加入新功能到COM DLL

        假设原来的DLL提供了加法和减法功能,现在需要添加一个乘法功能。

#include <windows.h>
#include <Unknwnbase.h>

// Interface ID (IID) for IMathOperations
// {12345678-1234-1234-1234-123456789012}
static const IID IID_IMathOperations =
{ 0x12345678, 0x1234, 0x1234, { 0x12, 0x34, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12 } };

// Class ID (CLSID) for MathOperations
// {87654321-4321-4321-4321-210987654321}
static const CLSID CLSID_MathOperations =
{ 0x87654321, 0x4321, 0x4321, { 0x43, 0x21, 0x21, 0x09, 0x87, 0x65, 0x43, 0x21 } };

// Define the IMathOperations interface
class IMathOperations : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE Add(int a, int b, int* result) = 0;
    virtual HRESULT STDMETHODCALLTYPE Subtract(int a, int b, int* result) = 0;
};


// Interface ID (IID) for IMathOperations2
// {98765432-4321-4321-4321-123456789012}
static const IID IID_IMathOperations2 =
{ 0x98765432, 0x4321, 0x4321, { 0x43, 0x21, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12 } };

class IMathOperations2 : public IMathOperations
{
public:
    virtual HRESULT STDMETHODCALLTYPE Multiply(int a, int b, int* result) = 0;
};

 LoadDLL:这里注意:

        IMathOperations2* pMathOps = nullptr; 这个指针不能生命错了,需要是新更新的类,更新的类里有继承老的类,所以这个IMathOperations2*调用老方法也没问题。

#include <iostream>
#include <windows.h>
#include "../COMTest/ComTest.h"  // 包含COM接口定义

// 定义函数指针类型
typedef HRESULT(*PFN_CREATE_INSTANCE)(REFIID riid, void** ppv);

int main() {
    HMODULE hDll = LoadLibrary(TEXT("COMTest.dll"));  // 动态加载DLL
    if (hDll == NULL) {
        std::cout << "Failed to load DLL." << std::endl;
        return -1;
    }

    // 获取函数指针
    PFN_CREATE_INSTANCE pfnCreateInstance = (PFN_CREATE_INSTANCE)GetProcAddress(hDll, "CreateInstance");
    if (pfnCreateInstance == NULL) {
        std::cout << "Failed to get function address." << std::endl;
        FreeLibrary(hDll);
        return -1;
    }

    HRESULT hr = CoInitialize(NULL);  // 初始化COM库
    if (FAILED(hr)) {
        std::cout << "Failed to initialize COM library." << std::endl;
        FreeLibrary(hDll);
        return -1;
    }

    IMathOperations2* pMathOps = nullptr;  // 指向接口的指针

    // 创建组件实例
    hr = pfnCreateInstance(IID_IMathOperations2, (void**)&pMathOps);
    if (SUCCEEDED(hr)) {
        int result = 0;

        // 调用Add方法
        hr = pMathOps->Add(5, 3, &result);
        if (SUCCEEDED(hr)) {
            std::cout << "Addition Result: " << result << std::endl;
        }

        // 调用Subtract方法
        hr = pMathOps->Subtract(5, 3, &result);
        if (SUCCEEDED(hr)) {
            std::cout << "Subtraction Result: " << result << std::endl;
        }

        hr = pMathOps->Multiply(5, 3, &result);
        if (SUCCEEDED(hr)) {
            std::cout << "Subtraction Result: " << result << std::endl;
        }

        // 释放接口
        pMathOps->Release();
    }
    else {
        std::cout << "Failed to create component instance." << std::endl;
    }

    CoUninitialize();  // 清理COM
    FreeLibrary(hDll); // 释放DLL
    system("pause");
    return 0;
}

出现的问题及其成因

  1. GetProcAddress返回空指针:

    • 原因可能包括函数名称修饰(name mangling)错误、DLL未正确加载或者指定的函数未正确导出。解决方法包括使用extern "C"来避免C++的名称修饰,并确保使用__declspec(dllexport)正确导出函数。
  2. COM接口的IID定义和使用:

    • 在C++中定义新接口时需要提供全新的IID。这是因为每个COM接口必须有一个全局唯一的标识符。未正确处理这一点可能导致接口不可识别或引用错误。
  3. 代码示例中缺少导出声明:

    • 最初的示例中未包括__declspec(dllexport),这导致了动态链接库中的函数不能被外部访问。这反映了在COM开发过程中对DLL导出规则的关注不足。
  4. 保持向后兼容性的方法:

    • 展示了如何通过继承和扩展现有COM接口来添加新功能,而不会影响依赖旧接口的现有应用程序。这是软件维护中非常重要的策略,以避免引入破坏性变更。

         COM编出来的dll可维护性极高。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/561143.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

开源贡献代码之​探索一下CPython

探索一下Cython 本篇文章将会围绕最近给Apache提的一个feature为背景&#xff0c;展开讲讲CPython遇到的问题&#xff0c;以及尝试自己从0写一个库出来&#xff0c;代码也已经放星球了&#xff0c;感兴趣的同学可以去下载学习。 0.背景 最近在给apache arrow提的一个feature因为…

【做一名健康的CSDNer】程序员如何早日脱单?

程序员脱单的策略可以从以下几个方面着手&#xff1a; 拓展社交圈&#xff1a;参加技术交流会、行业聚会、开源社区活动等&#xff0c;不仅可以提升技术能力&#xff0c;还可以结识更多志同道合的人&#xff0c;其中可能就包括潜在的伴侣65。 改善形象和性格&#xff1a;注意个…

【GIS教程】ArcGIS做日照分析(附练习数据下载)

我国对住宅日照标准的规定是:冬至日住宅底层日照不少于1小时或大寒日住宅层日照不少于2小时(通常以当地冬至日正午12时的太阳高度角作为依据)。因冬至日太阳高度角最低&#xff0c;照射范围最小&#xff0c;如果冬至日12&#xff1a;00建筑物底层能够接收到阳光&#xff0c;那么…

探索边缘计算:技术的新疆界

探索边缘计算&#xff1a;技术的新疆界 在当今迅速发展的数字化时代&#xff0c;云计算作为数据处理的主力军已广泛应用。但是&#xff0c;随着物联网&#xff08;IoT&#xff09;设备的急剧增加和数据生成速率的加快&#xff0c;云计算面临着种种挑战。边缘计算因此诞生&…

python爬虫-----深入了解 requests 库下篇(第二十五天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

[阅读笔记15][Orca]Progressive Learning from Complex Explanation Traces of GPT-4

接下来是微软的Orca这篇论文&#xff0c;23年6月挂到了arxiv上。 目前利用大模型输出来训练小模型的研究都是在模仿&#xff0c;它们倾向于学习大模型的风格而不是它们的推理过程&#xff0c;这导致这些小模型的质量不高。Orca是一个有13B参数的小模型&#xff0c;它可以学习到…

从零自制docker-11-【pivotRoot切换实现文件系统隔离】

文章目录 busyboxdocker run -d busybox topcontainerId(docker ps --filter "ancestorbusybox:latest"|grep -v IMAGE|awk {print $1})docker export -o busybox.tar $containerId or sudo docker export 09bbf421d93f > ./busybox.tar tar -xvf busybox.tar -C …

修复vite中使用react提示Fast refresh only works when a file only exports components.

前言 我通过 vite 构建了一个 react 应用并使用 react.lazy 来懒加载组件&#xff0c;但是在使用过程中 一直提示 Fast refresh only works when a file only exports components. Move your component(s) to a separate file.eslint(react-refresh/only-export-components)。…

编译OpenWRT固件

前言 编译环境&#xff0c;我是使用Ubuntu16.04.07 LTS 64位版 1.安装Ubuntu16.04.07 LTS 64 作者写这篇文章的时候lede源码使用debian11编译&#xff0c;对于的就是Ubuntu 20&#xff0c;至于为什么要安装ub16是因为最开始我不清楚要使用ub20安装&#xff0c;用ub16安装的时…

CCF-CSP真题《202312-2 因子化简》思路+python,c++满分题解

想查看其他题的真题及题解的同学可以前往查看&#xff1a;CCF-CSP真题附题解大全 试题编号&#xff1a;202312-2试题名称&#xff1a;因子化简时间限制&#xff1a;2.0s内存限制&#xff1a;512.0MB问题描述&#xff1a; 题目背景 质数&#xff08;又称“素数”&#xff09;是指…

RAG部署 | 使用TensorRT-LLM在Windows上部署检索增强生成聊天机器人RAG

项目应用场景 面向 Windows 平台部署 RAG 检索增强生成聊天机器人场景&#xff0c;项目采用 TensorRT-LLM 进行 GPU 加速推理&#xff0c;注意项目需要 RT4090 及以上的英伟达显卡支持。 项目效果 项目细节 > 具体参见项目 README.md (1) 下载构建好的 Llama2 TensorRT 模型…

Web开发:ASP.NET CORE的前端demo(纯前端)

目录 一、建立项目 二、删除无用文件 三、样式添加 四、写一个登录页面 五、登录主界面 一、建立项目 二、删除无用文件 三、样式添加 将你的图片资源添加在wwwroot下方&#xff0c;例如pics/logo.png 四、写一个登录页面 将Privacy.cshtml改为 Forget.cshtml &#xff0…

AJAX——图书管理案例

1.渲染列表 自己的图书数据&#xff1a;给自己起个外号&#xff0c;并告诉服务器&#xff0c;默认会有三本书&#xff0c;基于这三本书做数据的增删改查。 // 目标1&#xff1a;渲染图书列表 // 1.1 获取数据 // 1.2 渲染数据const creator 哈哈 // 封装-获取并渲染图书列表函…

设计模式学习笔记 - 开源实战三(中):剖析Google Guava中用到的设计模式

概述 上篇文章&#xff0c;我通过 Google Guava 这样一个优秀的开源类库&#xff0c;讲解了如何在业务开发中&#xff0c;发现跟业务无关、可以复用的通用功能模块&#xff0c;并将它们抽离出来&#xff0c;设计成独立的类库、框架或功能组件。 本章再来学习下&#xff0c;Go…

[Linux][进程信号][二][信号如何被保存][信号处理][可重入函数]详细解读

目录 1.信号如何被保存&#xff1f;1.信号其他相关常见概念2.信号在内核中的表示3.sigset_t -- 本质是个位图4.信号集操作函数sigset_t&#xff1a;sigprocmask()sigpending() 5.思考6.使用 2.信号处理0.内核态和用户态1.内核空间和用户空间2.信号何时被处理&#xff1f;3.信号…

PSA Group EDI 需求分析

PSA集团&#xff08;以下简称PSA&#xff09;中文名为标致雪铁龙集团&#xff0c;是一家法国私营汽车制造公司&#xff0c;致力于为全球消费者提供独具特色的汽车体验和自由愉悦的出行方案&#xff0c;旗下拥有标致、雪铁龙、DS、欧宝、沃克斯豪尔五大汽车品牌。 汽车制造企业对…

JavaWeb--前端--02JavaScript

JavaScript 1 JavaScript介绍2 引入方式3 基础语法3.1 书写语法3.2 变量3.3 数据类型和运算符 4 JS的函数4.1函数的第一种定义4.2 函数的第二中定义 5 JavaScript对象5.1 基本对象5.1.1 Array对象5.1.2 String对象5.1.3 Json对象 5.2 BOM5.2.1 BOM对象5.2.1 Windows对象5.2.2 L…

c++补充

构造函数、析构函数 #include <iostream> using namespace std;// 构造函数、析构函数 // --- "构造函数"类比生活中的"出厂设置" --- // --- "析构函数"类比生活中的"销毁设置" --- // 如果我们不写这两种函数&#xff0c;编译…

定制k8s域名解析------CoreDns配置实验

定制k8s域名解析------CoreDns配置实验 1. 需求 k8s集群内通过CoreDns互相解析service名. 同时pana.cn域为外部dns解析,需要通过指定dns服务器进行解析 再有3个服务器,需要使用A记录进行解析 2. K8s外DNS服务器 查看解析文件 tail -3 /var/named/pana.cn.zone 解析内容 ww…

STM32G431RBT6之时钟树配置与生成工程

默认大家都下载了蓝桥杯嵌入式资源包了哈. 首先,打开cubumx,修改RCC与SYS. 打开并观察原理图,发现晶振是24Mhz. 第一步,打开Clock Configuration. 第二步,修改晶振为原理图相对应的24Mhz. 第三步,切换到HSE. 第四步,切换到PLLCLK. 第五步,设置HCLK为80Mhz(15届真题要求为8…