转自:https://blog.csdn.net/shejiannan/article/details/26386271

最近在学习COM,自己实现了一个小Demo,拿出来和大家分享一下。求各种批评。

我实现的这个组件向外提供了一个接口ICompTest,里面只有一个函数helloworld(),功能为返回一个整数89。

实现了自注册功能。下面贴出代码,希望对刚开始学习COM的朋友有所帮助。

首先看一下工程结构,编译环境为vs 2008

CompTest工程是服务端工程,是一个dll,CtrlTest是客户端工程,是一个控制台工程。

下面通过客户端的运行逻辑来讲述整个运行流程,先看一下CtrlTest.cpp文件

[cpp] view plaincopy
  1. /**
  2. *  @file CtrlTest.cpp
  3. *  @author LiWang112358
  4. *  @date 2012/3/17
  5. *  @version 1.0
  6. *  @brief COM客户端代码
  7. */
  8. #include <iostream>
  9. #include "ICompTest.h"
  10. using namespace std;
  11. int main()
  12. {
  13. CoInitialize(NULL);     //初始化COM库,使用默认的内存分配器
  14. IUnknown* pUnknown = NULL;
  15. GUID CLSID_CompTestClass;
  16. HRESULT hResult = CLSIDFromProgID(L"COMCTL.CompTest", &CLSID_CompTestClass);    //获取ProgID为COMCTL.CompTest组建的CLSID
  17. if (S_OK != hResult){
  18. printf("Can't find CLSID!\n");
  19. return -1;
  20. }
  21. else{
  22. LPOLESTR szCLSID;
  23. StringFromCLSID(CLSID_CompTestClass, &szCLSID);     //将其转化为字符串形式用来输出
  24. wprintf(L"find CLSID \"%s\"\n",szCLSID);
  25. CoTaskMemFree(szCLSID);     //调用COM库的内存释放
  26. }
  27. //用此CLSID创建一个COM对象并获取IUnknown接口
  28. hResult = CoCreateInstance(CLSID_CompTestClass, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown,(void **)&pUnknown);
  29. if (S_OK != hResult || NULL == pUnknown){
  30. printf("Create Object Failed!\n");
  31. return -1;
  32. }
  33. ICompTest* pCompTest = NULL;
  34. hResult = pUnknown->QueryInterface(IID_ICompTest, (void**)&pCompTest);//通过此结构查询我们自己的ICompTest接口
  35. cout << pCompTest->HelloWorld() << endl;//调用我们自己接口中的函数
  36. pCompTest->Release();    //释放自己的接口
  37. pUnknown->Release(); //释放IUnknown接口
  38. CoUninitialize();       //COM库反初始化
  39. return 0;
  40. }

这是客户程序的主逻辑,主要就是通过COM库创建CompTestClass对象,这个对象在这里是不可见的,这里只能拿到ICompTest接口,通过该接口调用函数HelloWorld。

下面看一下接口的声明ICompTest.h文件,这个文件是客户端和服务端都要有的。

[cpp] view plaincopy
  1. /**
  2. *  @file ICompTest.h
  3. *  @author LiWang112358
  4. *  @date 2012/3/17
  5. *  @version 1.0
  6. *  @brief ICompTest接口的声明这个文件是服务端和客户端共有的
  7. */
  8. #ifndef ICOMPTEST_H
  9. #define ICOMPTEST_H
  10. #include <unknwn.h>
  11. // {81A80687-6CC4-4996-8DD2-F058907FDCA8}
  12. static const GUID IID_ICompTest =
  13. { 0x81a80687, 0x6cc4, 0x4996, { 0x8d, 0xd2, 0xf0, 0x58, 0x90, 0x7f, 0xdc, 0xa8 } };
  14. class ICompTest :
  15. public IUnknown
  16. {
  17. public:
  18. virtual int _stdcall HelloWorld() = 0;
  19. };
  20. #endif

这个文件中有一个GUID IID_ICompTest ,用于查询接口ICompTest 。

我们可以在 CtrlTest.cpp文件中看到CLSIDFromProgID和CoCreateInstance这两个函数,

第一个函数是要通过一个名字"COMCTL.CompTest"拿到一个CLSID,这个过程需要CLSID信息。

第二个函数是要通过这个CLSID找到我们的组件(dll),并加载这个dll,然后创建COM对象,这个过程需要dll的路径信息。

这些信息都被放在注册表中,是这个组件自注册的时候由DllRegisterServer函数写入的。

我们可以先看一下注册之后注册表中的内容

其中COMCTL.CompTest是在键HKEY_CLASSES_ROOT下,{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}是在键HKEY_CLASSES_ROOT\CLSID下。

下面我们看一下这个dll的注册过程。

用“regsvr32.exe dll路径”对dll进行注册,实际上regsvr32只是调用了dll中的DllRegisterServer引出函数。

下面我们看一下DllRegisterServer函数的实现,这个函数在CompTest.cpp中。

[cpp] view plaincopy
  1. int myReg(LPCWSTR lpPath)   //将本组件的信息写入注册表,包括CLSID、所在路径lpPath、ProgID
  2. {
  3. HKEY thk, tclsidk;
  4. //打开键HKEY_CLASSES_ROOT\CLSID,创建新键为CompTestClass的CLSID,
  5. //在该键下创建键InprocServer32,并将本组件(dll)所在路径lpPath写为该键的默认值
  6. if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"CLSID", &thk)){
  7. if (ERROR_SUCCESS == RegCreateKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}", &tclsidk)){
  8. HKEY tinps32k, tprogidk;
  9. if (ERROR_SUCCESS == RegCreateKey(tclsidk, L"InprocServer32", &tinps32k)){
  10. if (ERROR_SUCCESS == RegSetValue(tinps32k, NULL, REG_SZ, lpPath, wcslen(lpPath) * 2)){
  11. }
  12. RegCloseKey(tinps32k);
  13. }
  14. RegCloseKey(tclsidk);
  15. }
  16. RegCloseKey(thk);
  17. }
  18. //在键HKEY_CLASSES_ROOT下创建新键为COMCTL.CompTest,
  19. //在该键下创建子键,并将CompTestClass的CLSID写为该键的默认值
  20. if (ERROR_SUCCESS == RegCreateKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest", &thk)){
  21. if (ERROR_SUCCESS == RegCreateKey(thk, L"CLSID", &tclsidk)){
  22. if (ERROR_SUCCESS == RegSetValue(tclsidk,
  23. NULL,
  24. REG_SZ,
  25. L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}",
  26. wcslen(L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}") * 2)){
  27. }
  28. }
  29. }
  30. //这样的话一个客户端程序如果想要使用本组件,首先可以以COMCTL.CompTest为参数调用CLSIDFromProgID函数
  31. //来获取CompTestClass的CLSID,再以这个CLSID为参数调用CoCreateInstance创建COM对象
  32. return 0;
  33. }
  34. extern "C" HRESULT _stdcall DllRegisterServer()
  35. {
  36. WCHAR szModule[1024];
  37. DWORD dwResult = GetModuleFileName(g_hModule, szModule, 1024); //获取本组件(dll)所在路径
  38. if (0 == dwResult){
  39. return -1;
  40. }
  41. MessageBox(NULL, szModule, L"", MB_OK);
  42. myReg(szModule);//将路径等信息写入注册表
  43. return 0;
  44. }

用“regsvr32.exe dll路径 -u”对dll进行反注册,同样,实际上regsvr32只是调用了dll中的DllUnregisterServer引出函数。

下面我们来看一下DllUnregisterServer函数的实现,这个函数在CompTest.cpp中。

[cpp] view plaincopy
  1. int myDelKey(HKEY hk, LPCWSTR lp)
  2. {
  3. if (ERROR_SUCCESS == RegDeleteKey(hk, lp)){
  4. }
  5. return 0;
  6. }
  7. int myDel() //删除注册时写入注册表的信息
  8. {
  9. HKEY thk;
  10. if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"CLSID", &thk)){
  11. myDelKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}\\InprocServer32");
  12. myDelKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}");
  13. RegCloseKey(thk);
  14. }
  15. if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest", &thk)){
  16. myDelKey(thk, L"CLSID");
  17. }
  18. myDelKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest");
  19. return 0;
  20. }
  21. extern "C" HRESULT _stdcall DllUnregisterServer()
  22. {
  23. myDel();//删除注册时写入注册表的信息
  24. return 0;
  25. }

我们继续分析客户端的代码CoCreateInstance(CLSID_CompTestClass, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown,(void **)&pUnknown);

这个函数是要调用CoGetClassObject函数,来获取CompTestClass的类厂,以此创建CompTestClass对象并获取IUnknown接口。其中,CoGetClassObject函数

实际上是调用了CompTest.cpp中的又一个引出函数DllGetClassObject来获取IClassFactory接口的。最终CoCreateInstance会调用IClassFactory接口的CreateInstance

函数去创建COM对象。

下面我们看一下DllGetClassObject函数的实现

[cpp] view plaincopy
  1. extern "C" HRESULT _stdcall DllGetClassObject(__in REFCLSID rclsid, __in REFIID riid, LPVOID FAR* ppv)//用于创建类厂并返回所需接口,由CoGetClassObject函数调用
  2. {
  3. if (CLSID_CompTestClass == rclsid){
  4. CompTestFactory* pFactory = new CompTestFactory();//创建类厂对象
  5. if (NULL == pFactory){
  6. return E_OUTOFMEMORY;
  7. }
  8. HRESULT result = pFactory->QueryInterface(riid, ppv);//获取所需接口
  9. return result;
  10. }
  11. else{
  12. return CLASS_E_CLASSNOTAVAILABLE;
  13. }
  14. }

接下来我们看一下组件中的最后一个引出函数DllCanUnloadNow,这样dll中的所有引出函数就都出现了

[cpp] view plaincopy
  1. extern "C" HRESULT _stdcall DllCanUnloadNow()//用于判断是否可以卸载本组建, 由CoFreeUnusedLibraries函数调用
  2. {
  3. if (0 == g_num){//如果对象个数为0,则可以卸载
  4. return S_OK;
  5. }
  6. else{
  7. return S_FALSE;
  8. }
  9. }

其中ULONG g_num表示组件中CompTestClass对象的个数,用于判断是否可以卸载本组建,如值为0则可以卸载

下面我们看一下CompTest.cpp的全貌

[cpp] view plaincopy
  1. /**
  2. *  @file CompTest.cpp
  3. *  @author LiWang112358
  4. *  @date 2012/3/17
  5. *  @version 1.0
  6. *  @brief 四个重要的引出函数的实现,
  7. *  @      分别是DllRegisterServer,用于注册本dll,由regsvr32.exe调用
  8. *  @            DllUnregisterServer, 用于反注册本dll,由regsvr32.exe -u 调用
  9. *  @            DllCanUnloadNow,用于判断是否可以卸载本组建, 由CoFreeUnusedLibraries函数调用
  10. *  @            DllGetClassObject,用于创建类厂并返回所需接口,由CoGetClassObject函数调用
  11. */
  12. #include <iostream>
  13. #include <windows.h>
  14. #include "factory.h"
  15. #include "CompTestClass.h"
  16. using namespace std;
  17. HMODULE g_hModule;  //dll进程实例句柄
  18. ULONG g_num;        //组件中CompTestClass对象的个数,用于判断是否可以卸载本组建,如值为0则可以卸载
  19. int myReg(LPCWSTR lpPath)   //将本组件的信息写入注册表,包括CLSID、所在路径lpPath、ProgID
  20. {
  21. HKEY thk, tclsidk;
  22. //打开键HKEY_CLASSES_ROOT\CLSID,创建新键为CompTestClass的CLSID,
  23. //在该键下创建键InprocServer32,并将本组件(dll)所在路径lpPath写为该键的默认值
  24. if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"CLSID", &thk)){
  25. if (ERROR_SUCCESS == RegCreateKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}", &tclsidk)){
  26. HKEY tinps32k, tprogidk;
  27. if (ERROR_SUCCESS == RegCreateKey(tclsidk, L"InprocServer32", &tinps32k)){
  28. if (ERROR_SUCCESS == RegSetValue(tinps32k, NULL, REG_SZ, lpPath, wcslen(lpPath) * 2)){
  29. }
  30. RegCloseKey(tinps32k);
  31. }
  32. RegCloseKey(tclsidk);
  33. }
  34. RegCloseKey(thk);
  35. }
  36. //在键HKEY_CLASSES_ROOT下创建新键为COMCTL.CompTest,
  37. //在该键下创建子键,并将CompTestClass的CLSID写为该键的默认值
  38. if (ERROR_SUCCESS == RegCreateKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest", &thk)){
  39. if (ERROR_SUCCESS == RegCreateKey(thk, L"CLSID", &tclsidk)){
  40. if (ERROR_SUCCESS == RegSetValue(tclsidk,
  41. NULL,
  42. REG_SZ,
  43. L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}",
  44. wcslen(L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}") * 2)){
  45. }
  46. }
  47. }
  48. //这样的话一个客户端程序如果想要使用本组件,首先可以以COMCTL.CompTest为参数调用CLSIDFromProgID函数
  49. //来获取CompTestClass的CLSID,再以这个CLSID为参数调用CoCreateInstance创建COM对象
  50. return 0;
  51. }
  52. extern "C" HRESULT _stdcall DllRegisterServer()
  53. {
  54. WCHAR szModule[1024];
  55. DWORD dwResult = GetModuleFileName(g_hModule, szModule, 1024); //获取本组件(dll)所在路径
  56. if (0 == dwResult){
  57. return -1;
  58. }
  59. MessageBox(NULL, szModule, L"", MB_OK);
  60. myReg(szModule);//将路径等信息写入注册表
  61. return 0;
  62. }
  63. int myDelKey(HKEY hk, LPCWSTR lp)
  64. {
  65. if (ERROR_SUCCESS == RegDeleteKey(hk, lp)){
  66. }
  67. return 0;
  68. }
  69. int myDel() //删除注册时写入注册表的信息
  70. {
  71. HKEY thk;
  72. if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"CLSID", &thk)){
  73. myDelKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}\\InprocServer32");
  74. myDelKey(thk, L"{9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}");
  75. RegCloseKey(thk);
  76. }
  77. if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest", &thk)){
  78. myDelKey(thk, L"CLSID");
  79. }
  80. myDelKey(HKEY_CLASSES_ROOT, L"COMCTL.CompTest");
  81. return 0;
  82. }
  83. extern "C" HRESULT _stdcall DllUnregisterServer()
  84. {
  85. myDel();//删除注册时写入注册表的信息
  86. return 0;
  87. }
  88. extern "C" HRESULT _stdcall DllCanUnloadNow()//用于判断是否可以卸载本组建, 由CoFreeUnusedLibraries函数调用
  89. {
  90. if (0 == g_num){//如果对象个数为0,则可以卸载
  91. return S_OK;
  92. }
  93. else{
  94. return S_FALSE;
  95. }
  96. }
  97. extern "C" HRESULT _stdcall DllGetClassObject(__in REFCLSID rclsid, __in REFIID riid, LPVOID FAR* ppv)//用于创建类厂并返回所需接口,由CoGetClassObject函数调用
  98. {
  99. if (CLSID_CompTestClass == rclsid){
  100. CompTestFactory* pFactory = new CompTestFactory();//创建类厂对象
  101. if (NULL == pFactory){
  102. return E_OUTOFMEMORY;
  103. }
  104. HRESULT result = pFactory->QueryInterface(riid, ppv);//获取所需接口
  105. return result;
  106. }
  107. else{
  108. return CLASS_E_CLASSNOTAVAILABLE;
  109. }
  110. }
  111. BOOL APIENTRY DllMain( HMODULE hModule,
  112. DWORD  ul_reason_for_call,
  113. LPVOID lpReserved
  114. )
  115. {
  116. g_hModule = hModule;//获取进程实例句柄,用于获取本组件(dll)路径
  117. switch (ul_reason_for_call)
  118. {
  119. case DLL_PROCESS_ATTACH:
  120. case DLL_THREAD_ATTACH:
  121. case DLL_THREAD_DETACH:
  122. case DLL_PROCESS_DETACH:
  123. break;
  124. }
  125. return TRUE;
  126. }

下面是.def文件mydef.def用于声明dll的引出函数

[cpp] view plaincopy
  1. LIBRARY "CompTest"
  2. EXPORTS
  3. DllCanUnloadNow
  4. DllGetClassObject
  5. DllUnregisterServer
  6. DllRegisterServer

下面是剩下的文件,有些许注释,语焉不详之处,望海涵。

CompTestClass.h

[cpp] view plaincopy
  1. /**
  2. *  @file CompTestClass.h
  3. *  @author LiWang112358
  4. *  @date 2012/3/17
  5. *  @version 1.0
  6. *  @brief CompTestClass对象的声明,这个类要实现ICompTest接口
  7. */
  8. #ifndef COMPTESTCLASS_H
  9. #define COMPTESTCLASS_H
  10. #include "ICompTest.h"
  11. // {9CA9DBE8-C0B1-42c9-B6C7-856BE5756855}
  12. static const GUID CLSID_CompTestClass =
  13. { 0x9ca9dbe8, 0xc0b1, 0x42c9, { 0xb6, 0xc7, 0x85, 0x6b, 0xe5, 0x75, 0x68, 0x55 } };
  14. class CompTestClass :
  15. public ICompTest
  16. {
  17. public:
  18. CompTestClass();
  19. ~CompTestClass();
  20. //要实现IUnknown接口
  21. virtual HRESULT _stdcall QueryInterface(const IID& riid, void** ppvObject);
  22. virtual ULONG _stdcall AddRef();
  23. virtual ULONG _stdcall Release();
  24. //要实现ICompTest接口
  25. virtual int _stdcall HelloWorld();
  26. protected:
  27. ULONG m_Ref;
  28. };
  29. #endif

CompTestClass.cpp

[cpp] view plaincopy
  1. /**
  2. *  @file CompTestClass.cpp
  3. *  @author LiWang112358
  4. *  @date 2012/3/17
  5. *  @version 1.0
  6. *  @brief CompTestClass类的实现
  7. */
  8. #include "CompTestClass.h"
  9. ULONG CompTestClass::m_objNum = 0;//组件中CompTestClass对象的个数,用于判断是否可以卸载本组建,如值为0则可以卸载
  10. CRITICAL_SECTION CompTestClass::m_cs;//为了多线程调用对m_objNum加的锁
  11. CompTestClass::CompTestClass()
  12. {
  13. m_Ref = 0;
  14. autoLock tlock(m_cs);
  15. m_objNum ++;    //构造了一个对象
  16. }
  17. CompTestClass::~CompTestClass()
  18. {
  19. autoLock tlock(m_cs);
  20. m_objNum --;    //释放了一个对象
  21. }
  22. HRESULT _stdcall CompTestClass::QueryInterface(const IID &riid, void **ppvObject)
  23. {
  24. if (IID_IUnknown == riid){
  25. *ppvObject = (IUnknown*)this;
  26. ((IUnknown*)(*ppvObject))->AddRef();
  27. }
  28. else if (IID_ICompTest == riid){
  29. *ppvObject = (ICompTest*)this;
  30. ((ICompTest*)(*ppvObject))->AddRef();
  31. }
  32. else{
  33. *ppvObject = NULL;
  34. return E_NOINTERFACE;
  35. }
  36. return S_OK;
  37. }
  38. ULONG _stdcall CompTestClass::AddRef()
  39. {
  40. m_Ref ++;
  41. return m_Ref;
  42. }
  43. ULONG _stdcall CompTestClass::Release()
  44. {
  45. m_Ref --;
  46. if (0 == m_Ref){
  47. delete this;
  48. return 0;
  49. }
  50. return m_Ref;
  51. }
  52. int _stdcall CompTestClass::HelloWorld()//ICompTest接口的实现,返回一个整数89
  53. {
  54. return 89;
  55. }
  56. int CompTestClass::Init()
  57. {
  58. m_objNum = 0;
  59. InitializeCriticalSection(&m_cs);
  60. return 0;
  61. }
  62. ULONG CompTestClass::ObjNum()
  63. {
  64. return m_objNum;
  65. }

factory.h

[cpp] view plaincopy
  1. /**
  2. *  @file factory.h
  3. *  @author LiWang112358
  4. *  @date 2012/3/17
  5. *  @version 1.0
  6. *  @brief CompTestClass对象的类厂的声明
  7. */
  8. #ifndef FACTORY_H
  9. #define FACTORY_H
  10. #include <unknwn.h>
  11. class CompTestFactory :
  12. public IClassFactory
  13. {
  14. public:
  15. CompTestFactory();
  16. ~CompTestFactory();
  17. //要实现IUnknown接口
  18. virtual HRESULT _stdcall QueryInterface(const IID& riid, void** ppvObject);
  19. virtual ULONG _stdcall AddRef();
  20. virtual ULONG _stdcall Release();
  21. //要实现IClassFactory接口
  22. virtual HRESULT _stdcall CreateInstance(IUnknown *pUnkOuter, const IID& riid, void **ppvObject);
  23. virtual HRESULT _stdcall LockServer(BOOL fLock);
  24. protected:
  25. ULONG m_Ref;
  26. };
  27. #endif

factory.cpp

[cpp] view plaincopy
  1. /**
  2. *  @file factory.cpp
  3. *  @author LiWang112358
  4. *  @date 2012/3/17
  5. *  @version 1.0
  6. *  @brief CompTestClass对象的类厂的实现
  7. */
  8. #include "factory.h"
  9. #include "CompTestClass.h"
  10. CompTestFactory::CompTestFactory()
  11. {
  12. m_Ref = 0;
  13. }
  14. CompTestFactory::~CompTestFactory()
  15. {
  16. }

C++ COM组件编写初探(上)相关推荐

  1. C++ COM组件编写初探

    转自:https://blog.csdn.net/shejiannan/article/details/26386271 最近在学习COM,自己实现了一个小Demo,拿出来和大家分享一下.求各种批评. ...

  2. java jsp 文件上传_用JSP编写文件上传

    用JSP编写文件上传 发布时间:2016-09-14 来源: 点击: 次 如果你曾用VB编写文件上传的组件的话,那么用JAVA编写文件上传的JAVABEAN十分容易. 下面的例子只是一个简版 pack ...

  3. Android组件化初探

    Android组件化初探 1. 现状 随着业务增加,代码量急剧增加,大大延长了编译时间,短则3min,长则7.8min:很多同学为节省开发时间,自己新建新的app,完成开发后,再合并到project中 ...

  4. b站React禹哥版视频笔记-React面向组件编程(上)

    目录 前言 一.开发者工具的安装 二.函数式组件 三.复习类相关知识 四.类式组件 五.组件实例的三大核心属性1:state 1.对state的理解 2.初始化state 3.react中的事件绑定 ...

  5. 17_微信小程序之抖音微视无限滑动视频列表自定义组件编写

    微信小程序之抖音微视无限滑动视频列表自定义组件编写 一.先上效果图 看到上面,你可能首先会想到,使用swiper然后将swiper的circular设置为true,那么,想象一下,假如视频很多的情况下 ...

  6. 微信小程序之使用vant-3组件Uploader文件上传

    效果图: 微信小程序之使用vant-3组件Uploader文件上传 1.把vant的weapp包加进来 2.在app.json文件里面"usingComponents"加载进来才能 ...

  7. el-upload上传组件,达到上传数量限制时隐藏上传框?

    先上图: 测试今天给提了一个建议,想让上传图片达到限制之后就不展示后面的上传框,看了眼官网的例子: 发给他之后让他选一个我照做,可是需求那边又想要既展示查看删除图标(也就是下面的),又想要不展示上传框 ...

  8. antd 上传进度_antd vue upload组件使用customRequest上传文件显示文件上传进度

    antd-vue上传文件upload组件使用自定义上传方法customRequest无法显示文件上传进度条,如下图红框内的进度条无法显示当前文件上传进度 于是,在网上搜索解决方案: 第一种解决方案是自 ...

  9. layui如何集成文件服务器,layui使用upload组件实现文件上传功能

    layui使用upload组件实现文件上传功能 发布时间:2020-05-22 17:25:25 来源:亿速云 阅读:309 作者:鸽子 背景:页面上一个按钮,点击弹出上传框,从按钮的方法代码开始写: ...

最新文章

  1. 基于互联网云脑架构分析百度的现状与未来
  2. 基础JavaScript_Day04
  3. 【VMware vSAN 6.6】5.8.自动化:vSAN硬件服务器解决方案
  4. 大数据治理工程师_大数据治理关键技术解析(转自EAWorld)
  5. WebBrowser控件禁用超链接转向、脚本错误提示、默认右键菜单和快捷键
  6. Flex2.0实现文件上传功能(服务器为ASP.NET)
  7. plsql轻量版异常机制
  8. 【SpringBoot 2】(七)请求处理——映射 常用注解 方法参数的小技巧
  9. 六度分离(HDU-1869)
  10. Apache HttpClient 4 3开发指南
  11. Logisim新手入门实验
  12. 实验四 数据选择器及其应用
  13. 深度系统安装移动硬盘启动_国产操作系统深度(deepin)系统安装教程
  14. LabVIEW开发气体调节器
  15. NOIP中的数学--第6课 排列与组合
  16. echart 设置折线红色警戒线
  17. 谢烟客---------Linux之Aho Weinberger Kernighan
  18. Linux技巧:使用Fsck命令修复损坏的分区
  19. python批量下载网页文件夹_Python抓取网页批量下载文件方法初探(正则表达式+BeautifulSoup) (转)...
  20. SAP 批量修改工艺路线

热门文章

  1. CentOS8服务器时间同步
  2. android关机闹钟设计思路
  3. DirectX的error LNK2019: 无法解析的外部符号 _Direct3DCreate9@4,该符号在函数 long __cdecl InitD3D(struct HWND__ *)
  4. java中的notify和notifyAll有什么区别?
  5. 李宏毅作业十二 Transfer Learning(迁移学习)
  6. Resources.getSystem() 和 getResources()
  7. 嵌入式优秀资源网址整理
  8. python—基础练习2
  9. 学会这五个邮件办公技巧,邮件秒变“私人秘书”【公司邮箱如何申请】
  10. setTimeout函数的参数