- 背景
前一段時間由於業務關系,須要在一個比較舊的系統編寫一個補丁。原系統使用VB編程,但如果要使用VB來實現這個補丁的內容,對於我這個並不怎么接觸VB的人來說太頭痛了。由於平時使用的是C#來開發,於是便想到能否用編寫.NET COM組件的方式來讓VB調用。想到這兩三下便寫了個.NET COM測試組件,用VB測試調用測試OK。接下來就是進行后續的開發,可等到開發完成后到了補丁打包環節卻傻眼了。原來的補丁打包工具在誕生時還不知道.NET為何物,所以根本沒有使用RegAsm來注冊.NET COM組件的功能。現在更改打補丁工具那是不現實的,可要使得VB調用.NET COM又必須使用RegAsm注冊,這下怎么辦呢?
- 解決方式
原打包工具是支持使用RegSvr32來注冊Com組件的,忽然想到能否使用RegSvr32來注冊.NET COM組件呢?印象中regsvr32在注冊DLL的時候會有個CallBack函數的,於是趕緊Google找找相關資料最終解決了這個問題。主要原理是寫一個代理的dll,在這個DLL的注冊響應函數DllRegisterServer里調用RegAsm來注冊.NET COM,同理在DllUnregisterServer里反注冊。
- 代理DLL代碼
stdafx.h
#pragma once
#include <windows.h>
#include <tchar.h>
Register.cpp
/*----------------------------------------------
此組件主要解決C#編寫的COM組件無法用RegSvr32注冊的問題
原理是增加一個代理DLL,在代理DLL注冊時,響應DllRegisterServer注冊函數,
在響應函數中使用RegAsm來注冊.Net Com組件,反注冊同理。
----------------------------------------------*/
#include "stdafx.h"
#define MAX_PATH 260
//全局變量,存放注冊代理dll路勁
TCHAR g_FilePath[MAX_PATH];
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
//記錄代理DLL的路徑,主要是為了方便后面獲取代理DLL所在目錄
GetModuleFileName(hModule, g_FilePath, 255);
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//注冊響應函數
extern "C" _declspec(dllexport) long DllRegisterServer(void)
{
return Register(TRUE);
}
//反注冊響應函數
extern "C" _declspec(dllexport) long DllUnregisterServer(void)
{
return Register(FALSE);
}
//注冊組件
//install : TRUE for install, FALSE for unstall
BOOL Register(BOOL install){
TCHAR cmd[MAX_PATH];
PROCESS_INFORMATION processInfo;
STARTUPINFO stInfo;
//獲取注冊命令
GetCmdLine(cmd, install);
ZeroMemory( &processInfo, sizeof(processInfo) );
ZeroMemory( &stInfo, sizeof(stInfo) );
stInfo.cb = sizeof(stInfo);
BOOL ret;
//創建注冊進程
ret = CreateProcess(NULL,cmd,NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&stInfo,&processInfo);
if(ret==TRUE){
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
return TRUE;
}else{
//DWORD error = GetLastError();
return FALSE;
}
return FALSE;
}
//獲取注冊命令,加/s開關可以免除確認步驟。
//注冊: RegAsm.exe /s Test.dll
//反注冊:RegAsm.exe /s /u Test.dll
void GetCmdLine(LPTSTR cmd, BOOL install){
TCHAR regAsmPath[MAX_PATH];
TCHAR regDllPath[MAX_PATH];
TCHAR temp[10];
regAsmPath[0] = '\0';
regDllPath[0] = '\0';
temp[0] = '\0';
GetRegAsmPath(regAsmPath); //獲取RegAsm.exe程序絕對路徑
GetRegDllPath(regDllPath); //獲取需要注冊的.NET COM組件絕對路徑
if(install){
lstrcpy(temp, _T(" /s "));
}else{
lstrcpy(temp, _T(" /s /u "));
}
cmd[0] = '\0';
lstrcat(cmd, regAsmPath);
lstrcat(cmd, temp);
lstrcat(cmd, regDllPath);
}
//獲取需要注冊 .Net Com 組件路徑,此處寫死為Test.dll,與代理dll在同一個目錄
//后期可以改成讀取ini配置文件
void GetRegDllPath(LPTSTR path){
int count = 0;
for(int i=0; i < MAX_PATH; i++){
if(g_FilePath[i] == _T('\\')){
count = i + 2;
}
}
TCHAR temp[MAX_PATH];
lstrcpyn(temp, g_FilePath, count); //代理DLL所在目錄
lstrcat (path, _T("\""));
lstrcat (path, temp);
lstrcat (path, _T("Test.dll\""));
}
//獲取 .NET COM 注冊程序 RegAsm.exe 的路徑
//方法:讀取[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\InstallRoot]的值,
//然后拼接上"v2.0.50727\RegAsm.exe"
void GetRegAsmPath(LPTSTR path){
HKEY hKey;
DWORD dwBufLen = MAX_PATH;
LONG lRet;
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\.NETFramework"),0, KEY_READ, &hKey);
lRet = RegQueryValueEx(hKey, _T("InstallRoot"), NULL, NULL, (LPBYTE)path, &dwBufLen);
RegCloseKey(hKey);
lstrcat(path, _T("v2.0.50727\\RegAsm.exe"));
}