`

Com总结二

 
阅读更多

  一. HRESULT

      对于该返回值,只要记住一点,一定要使用SUCCEEDED和FAILED宏来判断,不要直接把它与S_OK,S_FALSE等等来比较。

 

 二. GUID

    包含头文件OBJBASE.H。   由于GUID值占用了16个字节,因此一般不用值传递GUID参数。而大量使用的是按引用传递,这就是为什么QueryInterface接受一个常量引用参数的原因。除了使用const IID&,还可以等价使用REFIID,同理,在传递类标识符时,可以使用REFCLSID,在传递GUID时可以使用REFGUID。

 

 三. 注册表

    由于Dll知道它所包含的组件,因此Dll可以完成这些信息的注册。但是由于Dll本身不能完成任何事情,因此在Dll中一定要输出如下两个函数:

     

STDAPI DllRegisterServer();
STDAPI DllUnregisterServer();

 四. Com库初始化

    在使用Com库中的其它函数之前,进程必须先调用CoInitialize来初始化Com库函数。当进程不再需要使用Com库函数时,必须调用CoUninitialize。对于每一个进程,Com库函数只需要初始化一次,并且由于Com库是用于创建组件的,因此进程中组件无需初始化Com库。

 

 五. 内存管理

    如果在组件中分配了一块儿内存,然后通过一个函数的指针传递给了客户,那么这块儿内存该由谁来释放,如何释放?

    为此,com提供了两个方便的函数,CoTaskMemAlloc和CoTaskMemFree。

void* CoTaskMemAlloc( ULONG cb);

void* CoTaskMemFree(void* pv);

    同某个输出参数相关联的内存的释放应由函数的调用者使用CoTaskMemFree完成。

 

 六.类厂

  CoCreateInstance 返回组件中某个接口

  CoGetClassObject 返回类厂中某个接口

  在每次创建组件时,先创建相应的类厂,然后用所获取的IClassFactory指针来创建所需的接口需要完成的工作显然比直接调用CoCreateInstance来创建所需的组件要复杂的多。CoCreateInstance实际上是通过CoGetClassObject实现的。源码如下:

HRESULT CoCreateInstance( const CLSID& clsid,
	                      IUnknown* pUnknownOuter,
						  DWORD dwClsContext,
						  const IID& iid,
						  void** ppv)
{
	*ppv = NULL;

	//先获得类厂接口指针
	IClassFactory* pIFactory = NULL;
	HRESULT hr = CoGetClassObject(clsid,dwClsContext,NULL,IID_IClassFactory,(void**)&pIFactory);
	//然后创建相应的组件
	if( SUCCEEDED(hr) )
	{
		hr = pIFactory->CreateInstance(pUnknownOuter,iid,ppv); //获取组件
		pIFactory->Release(); // 释放类厂接口,开销!
	}

	return hr;
}

   大多数情况下,组件的创建均使用CoCreateInstance而不是使用CoGetClassObject。但是在如下两种情况下应使用CoGetClassObject。

      v1:若不想使用IClassFactory接口来创建组件,比如想使用IClassFactory2来创建组件。(应该CoCreateInstance默认使用IClassFactory)

     v2:若需创建同一组件的多个实例,那么使用CoGetClassObject将可以获得更高的效率,因为这样只需要创建相应的类厂一次。

    (1) 类厂的特性

     首先类厂的一个实例只能创建同某个CLSID相应的组件。

    (2) 类厂的实现

     与某个特定的CLISID相应的类厂将是由实现组件的开发人员实现的。大多数情况下,类厂组件包含在与它所创建的组件相同的DLL中。 

    v1: DllGetClassObject

     CoGetClassObject主要是调用组件dll中输出的DllGetClassObject来获得类厂接口指针。该函数如下:

STDAPI DllGetClassObject( 
            const CLSID& clsid,
            const IID& iid,
            void** ppv);

    组件的创建过程:首先是客户,它将调用CoGetClassObject来启动组件的创建过程。其次是Com库,它实现了CoGetClassObject函数。第三个角色是DLL,其中实现了被CoGetClassObject调用的DllGetClassObject函数。DllGetClassObject的任务就是创建客户所请求的类厂。

    同一个DLL可以创建多个组件,这一点的关键之处在于将待创建的组件的CLISID传给DllGetClassObject,对于每一个CLSID,DllGetClassObject可以方便的创建一个不同的类厂。

 

 下面我们来实现一个通过类厂,而且提供注册功能的组件。

  (1)新建一个空的Dll项目。

  (2)定义接口文件IFace.h如下:

interface IX : IUnknown
{
	virtual void __stdcall Fx() = 0;
};

interface IY : IUnknown
{
	virtual void __stdcall Fy() = 0;
};

interface IZ : IUnknown
{
	virtual void __stdcall Fz() = 0;
};

extern "C" const IID IID_IX;
extern "C" const IID IID_IY;
extern "C" const IID IID_IZ;
extern "C" const CLSID CLSID_Component1 ;

 (3)定义guid的定义文件 GUIDS.cpp

#include <ObjBase.h>

// {32bb8320-b41b-11cf-a6bb-0080c7b2d682}
extern "C" const IID IID_IX = 
{0x32bb8320, 0xb41b, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;

// {32bb8321-b41b-11cf-a6bb-0080c7b2d682}
extern "C" const IID IID_IY = 
{0x32bb8321, 0xb41b, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;

// {32bb8322-b41b-11cf-a6bb-0080c7b2d682}
extern "C" const IID IID_IZ = 
{0x32bb8322, 0xb41b, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;

// {0c092c21-882c-11cf-a6bb-0080c7b2d682}
extern "C" const CLSID CLSID_Component1 =
{0x0c092c21, 0x882c, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;

   (3) 定义并实现注册Dll的文件

#ifndef __Registry_H__
#define __Registry_H__
//
// Registry.h
//   - Helper functions registering and unregistering a component.
//

// This function will register a component in the Registry.
// The component calls this function from its DllRegisterServer function.
HRESULT RegisterServer(HMODULE hModule, 
                       const CLSID& clsid, 
                       const char* szFriendlyName,
                       const char* szVerIndProgID,
                       const char* szProgID) ;

// This function will unregister a component.  Components
// call this function from their DllUnregisterServer function.
HRESULT UnregisterServer(const CLSID& clsid,
                         const char* szVerIndProgID,
                         const char* szProgID) ;

#endif

    

//
// Registry.cpp
//

#include <objbase.h>
#include <assert.h>

#include "Registry.h"

////////////////////////////////////////////////////////
//
// Internal helper functions prototypes
//

// Set the given key and its value.
BOOL setKeyAndValue(const char* pszPath,
                    const char* szSubkey,
                    const char* szValue) ;

// Convert a CLSID into a char string.
void CLSIDtochar(const CLSID& clsid, 
                 char* szCLSID,
                 int length) ;

// Delete szKeyChild and all of its descendents.
LONG recursiveDeleteKey(HKEY hKeyParent, const char* szKeyChild) ;

////////////////////////////////////////////////////////
//
// Constants
//

// Size of a CLSID as a string
const int CLSID_STRING_SIZE = 39 ;

/////////////////////////////////////////////////////////
//
// Public function implementation
//

//
// Register the component in the registry.
//
HRESULT RegisterServer(HMODULE hModule,            // DLL module handle
                       const CLSID& clsid,         // Class ID
                       const char* szFriendlyName, // Friendly Name
                       const char* szVerIndProgID, // Programmatic
                       const char* szProgID)       //   IDs
{
	// Get server location.
	char szModule[512] ;
	DWORD dwResult =
		::GetModuleFileName(hModule, 
		                    szModule,
		                    sizeof(szModule)/sizeof(char)) ;
	assert(dwResult != 0) ;

	// Convert the CLSID into a char.
	char szCLSID[CLSID_STRING_SIZE] ;
	CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;

	// Build the key CLSID\\{...}
	char szKey[64] ;
	strcpy(szKey, "CLSID\\") ;
	strcat(szKey, szCLSID) ;
  
	// Add the CLSID to the registry.
	setKeyAndValue(szKey, NULL, szFriendlyName) ;

	// Add the server filename subkey under the CLSID key.
	setKeyAndValue(szKey, "InprocServer32", szModule) ;

	// Add the ProgID subkey under the CLSID key.
	setKeyAndValue(szKey, "ProgID", szProgID) ;

	// Add the version-independent ProgID subkey under CLSID key.
	setKeyAndValue(szKey, "VersionIndependentProgID",
	               szVerIndProgID) ;

	// Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT.
	setKeyAndValue(szVerIndProgID, NULL, szFriendlyName) ; 
	setKeyAndValue(szVerIndProgID, "CLSID", szCLSID) ;
	setKeyAndValue(szVerIndProgID, "CurVer", szProgID) ;

	// Add the versioned ProgID subkey under HKEY_CLASSES_ROOT.
	setKeyAndValue(szProgID, NULL, szFriendlyName) ; 
	setKeyAndValue(szProgID, "CLSID", szCLSID) ;

	return S_OK ;
}

//
// Remove the component from the registry.
//
LONG UnregisterServer(const CLSID& clsid,         // Class ID
                      const char* szVerIndProgID, // Programmatic
                      const char* szProgID)       //   IDs
{
	// Convert the CLSID into a char.
	char szCLSID[CLSID_STRING_SIZE] ;
	CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;

	// Build the key CLSID\\{...}
	char szKey[64] ;
	strcpy(szKey, "CLSID\\") ;
	strcat(szKey, szCLSID) ;

	// Delete the CLSID Key - CLSID\{...}
	LONG lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szKey) ;
	assert((lResult == ERROR_SUCCESS) ||
	       (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist.

	// Delete the version-independent ProgID Key.
	lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szVerIndProgID) ;
	assert((lResult == ERROR_SUCCESS) ||
	       (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist.

	// Delete the ProgID key.
	lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szProgID) ;
	assert((lResult == ERROR_SUCCESS) ||
	       (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist.

	return S_OK ;
}

///////////////////////////////////////////////////////////
//
// Internal helper functions
//

// Convert a CLSID to a char string.
void CLSIDtochar(const CLSID& clsid,
                 char* szCLSID,
                 int length)
{
	assert(length >= CLSID_STRING_SIZE) ;
	// Get CLSID
	LPOLESTR wszCLSID = NULL ;
	HRESULT hr = StringFromCLSID(clsid, &wszCLSID) ;
	assert(SUCCEEDED(hr)) ;

	// Covert from wide characters to non-wide.
	wcstombs(szCLSID, wszCLSID, length) ;

	// Free memory.
	CoTaskMemFree(wszCLSID) ;
}

//
// Delete a key and all of its descendents.
//
LONG recursiveDeleteKey(HKEY hKeyParent,           // Parent of key to delete
                        const char* lpszKeyChild)  // Key to delete
{
	// Open the child.
	HKEY hKeyChild ;
	LONG lRes = RegOpenKeyEx(hKeyParent, lpszKeyChild, 0,
	                         KEY_ALL_ACCESS, &hKeyChild) ;
	if (lRes != ERROR_SUCCESS)
	{
		return lRes ;
	}

	// Enumerate all of the decendents of this child.
	FILETIME time ;
	char szBuffer[256] ;
	DWORD dwSize = 256 ;
	while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL,
	                    NULL, NULL, &time) == S_OK)
	{
		// Delete the decendents of this child.
		lRes = recursiveDeleteKey(hKeyChild, szBuffer) ;
		if (lRes != ERROR_SUCCESS)
		{
			// Cleanup before exiting.
			RegCloseKey(hKeyChild) ;
			return lRes;
		}
		dwSize = 256 ;
	}

	// Close the child.
	RegCloseKey(hKeyChild) ;

	// Delete this child.
	return RegDeleteKey(hKeyParent, lpszKeyChild) ;
}

//
// Create a key and set its value.
//   - This helper function was borrowed and modifed from
//     Kraig Brockschmidt's book Inside OLE.
//
BOOL setKeyAndValue(const char* szKey,
                    const char* szSubkey,
                    const char* szValue)
{
	HKEY hKey;
	char szKeyBuf[1024] ;

	// Copy keyname into buffer.
	strcpy(szKeyBuf, szKey) ;

	// Add subkey name to buffer.
	if (szSubkey != NULL)
	{
		strcat(szKeyBuf, "\\") ;
		strcat(szKeyBuf, szSubkey ) ;
	}

	// Create and open key and subkey.
	long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT ,
	                              szKeyBuf, 
	                              0, NULL, REG_OPTION_NON_VOLATILE,
	                              KEY_ALL_ACCESS, NULL, 
	                              &hKey, NULL) ;
	if (lResult != ERROR_SUCCESS)
	{
		return FALSE ;
	}

	// Set the Value.
	if (szValue != NULL)
	{
		RegSetValueEx(hKey, NULL, 0, REG_SZ, 
		              (BYTE *)szValue, 
		              strlen(szValue)+1) ;
	}

	RegCloseKey(hKey) ;
	return TRUE ;
}

  

(4)定义实现组件,以及类厂,以及导出函数的文件

#include <iostream>
#include <ObjBase.h>
#include "IFace.h"
#include "REGISTRY.H" 
using namespace std;
void trace(const char* msg) { cout << msg << endl; }

//全局变量区
static HMODULE g_hModule = NULL; //dll module handle
static long g_cComponents = 0;   // 组件的活动数
static long g_cServerLocks = 0;  // count of locks ,针对类厂

//用于注册表
const char g_szFriendlyName[] = "Inside Com,chapter 7 exmple wll";
// Version-independent ProgID
const char g_szVerIndProgID[] = "InsideCOM.Chap07" ;
// ProgID
const char g_szProgID[] = "InsideCOM.Chap07.1" ;


//组件
class CA : public IX,
	public IY 
{
public:
	// IUnknown
	virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;
	virtual ULONG __stdcall AddRef() ;
	virtual ULONG __stdcall Release() ;

	// Interface IX
	virtual void __stdcall Fx() { cout << "Fx" << endl ;}

	// Interface IY
	virtual void __stdcall Fy() { cout << "Fy" << endl ;} 

	// Constructor
	CA() ;
	// Destructor
	~CA() ;

private:
	// Reference count
	long m_cRef ;
} ;

//
// Constructor
//
CA::CA() : m_cRef(1)
{ 
	InterlockedIncrement(&g_cComponents) ; 
}

//
// Destructor
//
CA::~CA() 
{ 
	InterlockedDecrement(&g_cComponents) ; 
	trace("Component:\t\t CA Destroy self.") ;
}

HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
{    
	if (iid == IID_IUnknown)
	{
		*ppv = static_cast<IX*>(this) ; //默认返回IX
	}
	else if (iid == IID_IX)
	{
		*ppv = static_cast<IX*>(this) ;
		trace("Component:\t\tReturn pointer to IX. WLL") ; 
	}
	else if (iid == IID_IY)
	{
		*ppv = static_cast<IY*>(this) ; 
		trace("Component:\t\tReturn pointer to IY. WLL") ; 
	}
	else
	{
		*ppv = NULL ;
		return E_NOINTERFACE ;
	}
	reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
	return S_OK ;
}

ULONG __stdcall CA::AddRef()
{
	return InterlockedIncrement(&m_cRef) ;
}

ULONG __stdcall CA::Release() 
{
	if (InterlockedDecrement(&m_cRef) == 0)
	{
		delete this ;
		return 0 ;
	}
	return m_cRef ;
}


///////////////////////////////////////////////////////////
//
// 类厂
//
class CFactory : public IClassFactory
{
public:
	// IUnknown
	virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;         
	virtual ULONG   __stdcall AddRef() ;
	virtual ULONG   __stdcall Release() ;

	// Interface IClassFactory
	virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter,
		const IID& iid,
		void** ppv) ;
	virtual HRESULT __stdcall LockServer(BOOL bLock) ; 

	// Constructor
	CFactory() : m_cRef(1) {}

	// Destructor
	~CFactory() { trace("Class factory:\t\tDestroy self.") ;}

private:
	long m_cRef ;
} ;

HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)
{    
	if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
	{
		*ppv = static_cast<IClassFactory*>(this) ; 
	}
	else
	{
		*ppv = NULL ;
		return E_NOINTERFACE ;
	}
	reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
	return S_OK ;
}

ULONG __stdcall CFactory::AddRef()
{
	return InterlockedIncrement(&m_cRef) ;
}

ULONG __stdcall CFactory::Release() 
{
	if (InterlockedDecrement(&m_cRef) == 0)
	{
		delete this ;
		return 0 ;
	}
	return m_cRef ;
}

//
// IClassFactory implementation
//
HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,
	const IID& iid,
	void** ppv) 
{
	trace("Class factory:\t\tCreate component.") ;

	// Cannot aggregate.
	if (pUnknownOuter != NULL)
	{
		return CLASS_E_NOAGGREGATION ;
	}

	// Create component.
	CA* pA = new CA ;
	if (pA == NULL)
	{
		return E_OUTOFMEMORY ;
	}

	// Get the requested interface.
	HRESULT hr = pA->QueryInterface(iid, ppv) ;

	// Release the IUnknown pointer.
	// (If QueryInterface failed, component will delete itself.)
	pA->Release() ;
	return hr ;
}

// LockServer
HRESULT __stdcall CFactory::LockServer(BOOL bLock) 
{
	if (bLock)
	{
		InterlockedIncrement(&g_cServerLocks) ; 
	}
	else
	{
		InterlockedDecrement(&g_cServerLocks) ;
	}
	return S_OK ;
}


//导出函数

//是否可以卸载Dll,提供给CoFreeUnusedLibraries库函数来调用。
STDAPI DllCanUnloadNow()
{
	if( g_cComponents == 0 && g_cServerLocks == 0 )
		return S_OK;
	else
		return S_FALSE;
}

//Get Class Factory
STDAPI DllGetClassObject(
	              const CLSID& clsid,
				  const IID& iid,
				  void** ppv)
{
	if( clsid != CLSID_Component1 )
		return CLASS_E_CLASSNOTAVAILABLE;

	CFactory* pFactory = new CFactory;

	if( pFactory == NULL )
		return E_OUTOFMEMORY;

	HRESULT hr = pFactory->QueryInterface(iid,ppv);
	pFactory->Release();
	return hr;
}

//
// DLL 注册到注册表
//
STDAPI DllRegisterServer()
{
	return RegisterServer(g_hModule, 
		CLSID_Component1,
		g_szFriendlyName,
		g_szVerIndProgID,
		g_szProgID) ;
}


//
// DLL 从注册表中删除
//
STDAPI DllUnregisterServer()
{
	return UnregisterServer(CLSID_Component1,
		g_szVerIndProgID,
		g_szProgID) ;
}

///////////////////////////////////////////////////////////
//
// DLL module information
//
BOOL APIENTRY DllMain(HANDLE hModule,
	                  DWORD dwReason,
	                  void* lpReserved)
{
	if (dwReason == DLL_PROCESS_ATTACH)
	{
		g_hModule = (HMODULE)hModule ;
	}
	return TRUE ;
}

    

  (6)定义导出函数文件

     

// CMPNT.def
LIBRARY         Cmpnt.dll
DESCRIPTION     'Chapter 7 Example COM Component (c)1996-1997 Dale E. Rogerson'

EXPORTS
                DllGetClassObject   @2	PRIVATE
                DllCanUnloadNow     @3	PRIVATE
                DllRegisterServer   @4	PRIVATE
                DllUnregisterServer @5	PRIVATE

  (7) build工程,并运行cmd后,输入 regsvr32 dll的绝对路径,进行dll注册。

 

   下面我们来实现测试工程,新建一个控制台程序。不需要拷贝dll,lib到工程目录。

    我们这里采用两种方式来访问组件,一个是CoCreateInstance,一个是CoGetClassObject

#include <iostream>
#include <objbase.h>

#include "Iface.h"
using namespace std;

void trace(const char* msg) { cout << "Client: \t\t" << msg << endl ;}

int main()
{
	// Initialize COM Library
	CoInitialize(NULL) ;

	trace("Call CoCreateInstance to create") ;
	trace("  component and get interface IX.") ;
	IX* pIX = NULL ; 

	//方式一:
	/*
	    HRESULT hr = ::CoCreateInstance(CLSID_Component1,
	    NULL, 
		CLSCTX_INPROC_SERVER,
		IID_IX, 
		(void**)&pIX) ;
	*/

	//方式二:
	IClassFactory* pIFactory = NULL;
	HRESULT hr = ::CoGetClassObject(CLSID_Component1,CLSCTX_INPROC_SERVER,NULL,IID_IClassFactory,(void**)&pIFactory);
	if( SUCCEEDED(hr) )
	{
		trace("Succeeded getting IFactory.") ;
		hr = pIFactory->CreateInstance(NULL,IID_IX,(void**)&pIX);
	}

	if (SUCCEEDED(hr))
	{
		trace("Succeeded getting IX.") ;
		pIX->Fx() ;          // Use interface IX.

		trace("Ask for interface IY.") ;
		IY* pIY = NULL ;
		hr = pIX->QueryInterface(IID_IY, (void**)&pIY) ;
		if (SUCCEEDED(hr))
		{
			trace("Succeeded getting IY.") ;
			pIY->Fy() ;       // Use interface IY.
			pIY->Release() ;
			trace("Release IY interface.") ;
		}
		else
		{
			trace("Could not get interface IY.") ;
		}

		trace("Ask for interface IZ.") ;

		IZ* pIZ = NULL ;
		hr = pIX->QueryInterface(IID_IZ, (void**)&pIZ) ;
		if (SUCCEEDED(hr))
		{
			trace("Succeeded in getting interface IZ.") ;
			pIZ->Fz() ;
			pIZ->Release() ;
			trace("Release IZ interface.") ;
		}
		else
		{
			trace("Could not get interface IZ.") ;
		}

		trace("Release IX interface.") ;
		pIX->Release() ;
	}
	else
	{
		cout << "Client: \t\tCould not create component. hr = "
			<< hex << hr << endl ;    
	}

	// Uninitialize COM Library
	CoUninitialize() ;

	return 0 ;
}

 

分享到:
评论

相关推荐

    struts2总结项目总结

    在本项目总结中,我们将深入探讨Struts2的核心特性、工作原理以及如何在实际项目中应用。 一、Struts2框架基础 1. 动态方法调用(Dynamic Method Invocation,DMI):Struts2允许通过URL直接调用Action类的方法,...

    C#调用COM组件方法总结

    C#调用COM组件方法总结 本篇文章总结了C#调用COM组件的方法,涵盖了将COM类型信息转换为.NET元数据、查看元数据、测试程序等多个步骤。下面是对应的知识点: 一、将COM类型信息转换为.NET元数据 在C#调用COM组件...

    年终总结PPT模板

    2. **1ppt.com的特点**: - **种类齐全**:该网站提供了多种类型的PPT模板,包括但不限于年终总结、项目报告、产品介绍等,满足不同场景的需求。 - **设计精美**:模板设计通常都非常专业,有助于提升汇报的整体...

    AC500串口COM2及Profibus通讯总结

    ### AC500串口COM2及Profibus通讯总结 #### 一、AC500 PLC串口通讯 ##### 1.1 COM2接口 **1.1.1 COM2与RS232** AC500系列PLC通过COM2端口支持RS232通讯协议。该接口主要用于与个人电脑(PC)或其他设备进行数据...

    项目总结模板PPT

    2. **PPT模板的作用**:PowerPoint(PPT)是一种常用的演示文稿制作软件,其模板设计简洁明了,可以快速构建结构化的项目总结报告。模板通常包括项目概述、目标达成情况、关键里程碑、问题与挑战、解决方案、团队...

    我的月工作总结——个人工作总结报告ppt模板.rar

    2. **遇到的问题**:在这个部分,你可以分享你在工作中遇到的难题,以及你是如何解决这些问题的。这不仅可以展现你的问题解决能力,也能为其他人提供可能遇到类似问题时的参考。 3. **个人总结**:这是对个人表现的...

    医院年度工作总结报告完整版ppt模板.rar

    使用这个"51pptmoban.com"模板,医院工作人员可以根据自身的实际情况填充内容,以专业且具有吸引力的方式展示医院的年度工作总结,同时也能确保报告结构的完整性和逻辑性。这样的模板既节省了制作时间,又保证了报告...

    jquery总结学习资料JQuery总结

    **jQuery 总结** 在实际开发中,jQuery 可以极大地提高工作效率,减少代码量,使得动态交互和界面美化变得更加简单。然而,随着 ES6 和现代前端框架的崛起,如 React 和 Vue,jQuery 在某些场景下可能不再是首选。...

    struts2学习笔记(完美总结)——转自OPEN经验库

    Action是Struts2中处理业务逻辑的核心组件,它是实现了`com.opensymphony.xwork2.Action`接口或其子接口的类。Action类通常包含了业务逻辑的处理方法,这些方法会被Struts2调用以响应用户的请求。Action类可以通过...

    struts2输入校验总结

    总结,Struts2的输入校验机制提供了手动和自动两种方式,结合配置文件和内置校验器,能够实现灵活且强大的数据校验,有效地防止了非法数据的流入,提升了系统的安全性。在实际开发中,开发者可以根据业务需求选择...

    JAVAEE期末项目总结报告.doc

    JAVAEE期末项目总结报告 本报告总结了JAVAEE期末项目的实现过程,涵盖了电子管理系统的设计和实现。该系统主要由两个模块组成:登录模块和电子管理模块,其中电子管理模块中有增加、删除、修改、查询模块。报告详细...

    ssh框架错误总结

    二、JAR包管理错误 在MyEclipse中增加JAR包时,需要注意JAR包的路径问题。如果JAR包的路径是绝对路径,将导致项目无法共享和提交到CVS服务器。 解决方法:关闭MyEclipse,使用Notepad打开项目目录下的.classpath...

    关于struts实验时的临时总结

    ### 关于Struts2实验时的临时总结 #### 概述 本文档是对Struts2实验过程中的几个关键问题及解决方案进行了归纳与整理。通过实际操作过程中遇到的问题及其解决办法,帮助其他开发者避免同样的错误,并提供高效的学习...

    struts2总结第二章

    Struts2是一个强大的MVC框架,它提供了许多功能来简化Web应用开发,包括类型转换、属性访问、文件上传以及拦截器等。以下是基于标题和描述的详细知识点: **一、局部类型转换** 在Struts2中,我们可以自定义类型...

    简约商务蓝工作总结述职通用ppt模板.zip

    2. 背景设计:模板采用了“云端美景大图背景”,这种背景图像通常寓意广阔的发展前景和远大理想,可以激发观众的想象力,同时为报告增添视觉吸引力。淡雅的灰背景则有助于突出文本和图形,避免色彩过于刺眼,干扰...

    md基础语法总结.md

    2. 第二 3. 第三 + 首先 + 其次 + 最后 ``` 效果: - 首先 - 其次 - 最后 1. 第一 2. 第二 3. 第三 + 首先 + 其次 + 最后 #### 四、引用 引用使用 `&gt;` 符号来标记,可以多层嵌套。 示例: ```markdown &gt; ...

    完整版上半年工作总结下半年工作计划ppt模板.rar

    此模板的文件名 "51pptmoban.com" 表明它可能来源于一个在线PPT模板网站,这样的资源对于不熟悉设计或者时间紧张的用户来说非常有帮助,他们可以快速定制出专业且具有吸引力的报告。 总的来说,这个PPT模板是IT工作...

    应届生ppt设计高手阿文年终个人总结.rar

    2. **设计技巧**:他可能详细讲解了一些高级的设计技巧,如动画效果的应用、图表的美化、讲故事的方式等。 3. **项目案例分析**:阿文可能列举了他在学习或工作中参与的实际项目,对每个项目的构思、设计过程和最终...

    简约大气商务蓝微立体工作汇报总结ppt模板.rar

    1. **微立体设计**:微立体是一种流行的设计风格,它在二维平面上创造出具有轻微立体感的效果。这种设计方式使得幻灯片的元素看起来更具有深度和层次感,使信息的展示更为生动,有助于吸引观众的注意力。 2. **简约...

    PPT达人学习成长感恩个人总结ppt模板.rar

    2. **实战演练**:通过实际制作PPT,反复练习以提高熟练度,同时探索不同设计风格。 3. **设计原则**:学习色彩理论、排版规则,提升设计感,使PPT更具吸引力。 4. **进阶技巧**:掌握自定义形状、动态图表、嵌入...

Global site tag (gtag.js) - Google Analytics