1. 程式人生 > >ATL 編寫控制元件 呼叫密碼鍵盤 全過程

ATL 編寫控制元件 呼叫密碼鍵盤 全過程

寫在前面:小哆把詳細過程貼出來,一方面是記錄備份,可以隨時再利用,另一方面是分享,希望後來的初學者可以輕鬆一些。

[這是一個整體,所以,今後就不在贅述其中的東西,除非是前期本人自己理解錯誤,會更正,同時特別備註]

沒有加入加密功能,MD5/DES加密都已實現,但在資源和其他的文章中有描述,本文加入過於贅述。

沒有數字簽名和釋出部分,還未完全明白。


使用軟體:Visual Studio 2010


呼叫:LOADDLL.dll



"檔案"
[選項]
(填寫名稱)

=============================


(1)建立專案

檔案 -> 新建 -> ATL專案 -> 填寫專案名稱"ActiveXTest" -> [應用程式型別]選擇[動態連結庫(DLL)] -> 完成

完成後,會在右邊[解決方案資源管理器]生成很多"標頭檔案.h"和"實現檔案.CPP",這些都是預設的不要修改。



(2)新增一個ALT簡單物件:

右鍵點選專案名稱"ActiveXTest"選擇 -> 新增[類] -> 選擇[ATL簡單物件] -> 新增 -> 簡稱(test_op) -> 下一步:其他不變,在[支援]中選擇[連線點]和[IE物件支援]和[IEI] -> 完成 -> 選擇[覆蓋]



(3)給"test_op"新增一個方法,以便WEB頁面呼叫。

[類檢視]選擇"itest_op"(灰色鑰匙圖示)滑鼠右鍵新增 -> [新增方法] -> 方法起名為"testFunc"
引數型別選擇LONG  引數名iFlag
  引數屬性選擇IN -> 新增
引數型別選擇BSTR* 引數名passw  引數屬性選擇RETVAL -> 點選完成。
屬性in指輸入引數,屬性out指輸出引數。

這樣在專案中就產生了如下變化:

在test_op.h標頭檔案中添加了一個(在最後一行):

STDMETHOD(testFunc)(LONG iFlag, BSTR* passWord);

並在test_op.cpp檔案中添加了一個實現類:

STDMETHODIMP Ctest_op::testFunc(LONG iFlag, BSTR* passWord)
{
// TODO: 在此新增實現程式碼

return S_OK;
}


(4)在test_op.h 檔案中,呼叫DLL類庫。整體程式碼如下(認為新增的以標註):

// test_op.h : Ctest_op 的宣告

#pragma once
#include "resource.h"       // 主符號

#include "ActiveXTest_i.h"
#include "_Itest_opEvents_CP.h"


#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Windows CE 平臺(如不提供完全 DCOM 支援的 Windows Mobile 平臺)上無法正確支援單執行緒 COM 物件。定義 _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA 可強制 ATL 支援建立單執行緒 COM 物件實現並允許使用其單執行緒 COM 物件實現。rgs 檔案中的執行緒模型已被設定為“Free”,原因是該模型是非 DCOM Windows CE 平臺支援的唯一執行緒模型。"
#endif


using namespace ATL;


// Ctest_op


class ATL_NO_VTABLE Ctest_op :

	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<Ctest_op, &CLSID_test_op>,
	public ISupportErrorInfo,
	public IConnectionPointContainerImpl<Ctest_op>,
	public CProxy_Itest_opEvents<Ctest_op>,
	public IObjectWithSiteImpl<Ctest_op>,
	public IDispatchImpl<Itest_op, &IID_Itest_op, &LIBID_ActiveXTestLib, /*wMajor =*/ 1, /*wMinor =*/ 0>

{
public:

	//*******begin*****以下三行實現定義 add by 莫小哆_ly

    	typedef int (_stdcall *testGetPwd)(int,char*,char*); //型別定義,對應LOADDLL.dll->LoadFunc方法

	HINSTANCE hInstLibrary;

    	testGetPwd _testGetPwd; //類對映

	//*******end*****

	Ctest_op()
	{
	//*******begin*****開始呼叫DLL add by 莫小哆_ly

        hInstLibrary = LoadLibrary(L"C:\\WINDOWS\\system32\\LOADDLL.dll");//相對路徑時把寫好的LOADDLL.dll檔案放在此專案生成的目錄debug下。

        if (hInstLibrary == NULL)
        {
        FreeLibrary(hInstLibrary);//資源釋放
        }else{
        }

        //呼叫方法,返回方法控制代碼。

        _testGetPwd = (testGetPwd)GetProcAddress(hInstLibrary, "LoadFunc");

	//*******end*****	
	}


DECLARE_REGISTRY_RESOURCEID(IDR_test_op)

BEGIN_COM_MAP(Ctest_op)
	COM_INTERFACE_ENTRY(Itest_op)
	COM_INTERFACE_ENTRY(IDispatch)
	COM_INTERFACE_ENTRY(ISupportErrorInfo)
	COM_INTERFACE_ENTRY(IConnectionPointContainer)
	COM_INTERFACE_ENTRY(IObjectWithSite)
END_COM_MAP()

BEGIN_CONNECTION_POINT_MAP(Ctest_op)
	CONNECTION_POINT_ENTRY(__uuidof(_Itest_opEvents))
END_CONNECTION_POINT_MAP()
// ISupportsErrorInfo
	STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{
	}

public:

	STDMETHOD(testFunc)(LONG iFlag, BSTR* passWord);
};

OBJECT_ENTRY_AUTO(__uuidof(test_op), Ctest_op)

(6)回到在test_op.cpp 檔案中,新增實現程式碼如下:

// test_op.cpp : Ctest_op 的實現

#include "stdafx.h"
#include "test_op.h"


//*****begin*****add by 莫小哆_ly

//#include "md5.h"//用於MD5加密,與本文無關,略去
#pragma comment(lib, "comsuppw.lib")
#include <comutil.h>

//*****end*****


// Ctest_op


STDMETHODIMP Ctest_op::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* const arr[] = 
	{
		&IID_Itest_op
	};

	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}

STDMETHODIMP Ctest_op::testFunc(LONG iFlag, BSTR* passWord)
{
	// TODO: 在此新增實現程式碼


	//*****begin*****add by 莫小哆_ly

	int iflag = (int)(iFlag);
	char* pwd = new char[7];//參考testFunc的約定長度。
	char* mac = new char[33];
	char *pwd_md5 = new char[32];
	
	_testGetPwd(iflag,pwd,mac);//這裡要求廠家提供的dll裡(int,char*,char*)
	
	//結尾加結束符,防止陣列溢位出現亂碼[燙]
	pwd[6]='\0';//密碼6位.0.1.2.3.4.5/pwd[6]->結束符
		
	//MD5加密
	//pwd_md5 = MD5String(pwd);


	//char*型轉換成BSTR*型別
	*passWord = _com_util::ConvertStringToBSTR(pwd);
	
	//*****end*****
	
	this->_AtlFinalRelease();
	return S_OK;
}


(7)生成DLL:
選[生成],在專案的Debug資料夾裡找到dll檔案"ActiveXTest.dll"
把LOADDLL.dll按照"test_op.h"裡的描述放置,相對路徑時可以放入Debug資料夾



(8)打包CAB檔案:

利用iexpress.exe軟體

1.準備好所有你用到的、呼叫的dll,外加1個寫好的inf檔案。[好吧,如果你不會,那小哆後面會一步步教你]

2.解壓後,雙擊iexpress.exe。

[create new self extraction directive file] -> 下一步
[create compressed files only(activex installs)] -> 下一步
[add] ->選中準備好的檔案 -> 下一步
[browse] - 選擇好路徑/命名/儲存 ->[store files long file name inside package] -> 下一步
[don't save] -> 下一步 -> 下一步 -> 完成


3.cab包就此生成。

PS:小哆根據專案經驗,總結出來的。所以肯定是可行的。

附註:setup.inf檔案:

[version]

signature="$CHICAGO{1}quot;

AdvancedINF=2.0

[Add.Code]

ActiveXTest.dll=ActiveXTest.dll

LOADDLL.dll=LOADDLL.dll

[install.files]

ActiveXTest.dll=ActiveXTest.dll

LOADDLL.dll=LOADDLL.dll

[ActiveXTest.dll]

file-win32-x86=thiscab

RegisterServer=yes

clsid={197E37FC-9916-4881-B766-D43FDD0376E8}

DestDir=11

FileVersion=1,0,0,1

[LOADDLL.dll]

file-win32-x86=thiscab

DestDir=11

FileVersion=1,0,0,1

[RegisterFiles]

%11%/ActiveXTest.dll


【說明】

①[Add.Code]中dll順序:

{舉例說明}最終生成和呼叫的ActiveXTest.dll,其內部呼叫了LOADDLL.dll。

內部順序是:

ActiveXTest.dll
LOADDLL.dll

②clsid={197E37FC-9916-4881-B766-D43FDD0376E8}

專案裡的idl檔案中,有一段這麼記錄:

library ActiveXTestLib
{
importlib("stdole2.tlb");
[
uuid(197E37FC-9916-4881-B766-D43FDD0376E8)
]
dispinterface _Itest_opEvents
{
properties:
methods:
};

那就選這個uuid吧,別問我為什麼,我也不知道

這個到底是什麼情況,我也沒搞清楚,反正選了個這個可以正常執行。



(8)htm檔案

<HTML>

	<HEAD>

		<TITLE>New Page</TITLE>

		<OBJECT id=CaluNumCtrl codeBase="ActiveXTest.CAB#version=1,0,0,1" classid="clsid:3968A893-A79C-4AB9-B2FD-73BA2B76FB23"></OBJECT>

		<script language="javascript">

			function doTest()

			{
				var pwd = CaluNumCtrl.testFunc(1);

				alert(pwd);
			}

		</script>

	</HEAD>

	<BODY>
		
		<input type="button" value="呼叫" id="btnOK" onclick="doTest();"></input>
		
	</BODY>

</HTML>


【附註】
<OBJECT id=CaluNumCtrl codeBase="test2.CAB#version=1,0,0,1" classid="clsid:225EDF53-A85F-4CB5-977D-69509C7E675C"></OBJECT>
CLSID 是 ActiveX 專案生成的序號,具體可以在專案的test_op.rgs 檔案中找到。

ForceRemove {3968A893-A79C-4AB9-B2FD-73BA2B76FB23} = s 'test_op Class'