C# 에서 C# 용으로 만들어진 DLL 이 아니고서는 C++ 이나 C , Delphi 등에서 쓰듯이 LoadLibrary 로 불러 쓰는 방법이 DllImport 말고는 방법이 없을까요?
물론 있습니다.
그 DllImport 를 이용하여 다음 세가지의 함수를 kernel32.dll 에서 import 해 온 다음 사용하면 됩니다.
가장 먼저 필요한 것은 LoadLibrary() , GetProcAddress(), FreeLibrary() 이 세가지가 되겠습니다.
그리고 이 세가지 함수는 다음 방법으로 제작하려는 class 내에 선언 해 주면 됩니다.
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
private extern static int LoadLibrary(string librayName);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress" ,CharSet = CharSet.Ansi)]
private extern static IntPtr GetProcAddress(int hwnd, string procedureName);
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
private extern static bool FreeLibrary(int hModule);
이제 kernel32.dll (물론 이 dll 은 고유한 것이라, 64bit OS 에서도 동일한 symbol 을 유지하고 있습니다) 에서 끌어온 위 함수들을 이용하여 동적으로 함수형들을 선언 해 주어야 겠죠.
이전에 만든 SHARERES.DLL 을 예로 들자면 다음과 같이 만들 수 있습니다.
// declares functions of shareres.dll
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr _getInstance();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void _freeInstance();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int _setData(IntPtr pMem, uint memSize);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int _getData(IntPtr pMem, uint memSize);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate uint _getDataSize();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr _getLastErrorMsg();
단순히 DllImport 하는 것과 달리, 불러올 함수의 호출방법 등을 정의 해 주어야 합니다.
그것이 바로 UnmanagedFunctionPointer() 입니다.
위와 같이 만들어진 함수 원형들은 다시 함수형태로 선언 해 주어야 합니다.
private _getInstance func_getInstance;
private _freeInstance func_freeInstance;
private _setData func_setData;
private _getData func_getData;
private _getDataSize func_getDataSize;
private _getLastErrorMsg func_getLastErrorMsg;
이제 선언된 함수들을 다시 LoadLibrary() 를 거쳐 각 함수의 실제 메모리에 존재하는 위치랑 맞춰 주어야 겠죠?
이렇게 합니다.
if (4 < nVoidPtrLen)
isOS64bit = true;
if (isOS64bit)
{
hModule = LoadLibrary("SHARERES64.DLL");
}
else
{
hModule = LoadLibrary("SHARERES.DLL");
}
// allocating all functions...
IntPtr pFuncAddr = IntPtr.Zero;
pFuncAddr = GetProcAddress(hModule, "getInstance");
func_getInstance = (_getInstance)Marshal.GetDelegateForFunctionPointer(pFuncAddr, typeof(_getInstance));
pFuncAddr = GetProcAddress(hModule, "freeInstance");
func_freeInstance = (_freeInstance)Marshal.GetDelegateForFunctionPointer(pFuncAddr, typeof(_freeInstance));
pFuncAddr = GetProcAddress(hModule, "setData");
func_setData = (_setData)Marshal.GetDelegateForFunctionPointer(pFuncAddr, typeof(_setData));
pFuncAddr = GetProcAddress(hModule, "getData");
func_getData = (_getData)Marshal.GetDelegateForFunctionPointer(pFuncAddr, typeof(_getData));
pFuncAddr = GetProcAddress(hModule, "getDataSize");
func_getDataSize = (_getDataSize)Marshal.GetDelegateForFunctionPointer(pFuncAddr, typeof(_getDataSize));
pFuncAddr = GetProcAddress(hModule, "getLastErrorMsg");
func_getLastErrorMsg = (_getLastErrorMsg)Marshal.GetDelegateForFunctionPointer(pFuncAddr, typeof(_getLastErrorMsg));
이제 여기서 의문이 드시는 분들이 계실 겁니다.
nVoidPtrLen 이란건 뭐고, 왜 이걸로 DLL 을 따로 로드할까?
이는 MS 의 dotNET 3.5 이상을 사용하는 C# 은 가상호스팅을 통해 32/64bit OS 에 상관 없이 구동되도록 만들어 졌기 때문입니다.
어찌보면 편리한 기능 이긴 합니다만 ..
대신 느려터진 속도 등은 모두 감안 해 주어야 하는 큰 짐이 있기도 하다는 점 ... 명심해야 겠습니다.
제가 볼땐 아주 향상된 인터프리터어 라고 하겠습니다 -_-;
nVoidPtrLen 은 단순히 IntPtr.Size 를 대입 한 것이고, 32bit OS 에서는 이 IntPtr 의 크기가 4바이트, 즉 - 4가 됩니다.
반면에 64bit OS 에서는 8이 됩니다. CPU 의 레지스터가 8바이트 크기를 가지기 때문 입니다.
그럼으로 만들어 져야 하는 DLL 은 32bit 와 64bit 가 되고, 이는 MinGW 에서 얼마든지 만들어 낼 수 있습니다.
표준 DLL 생산은 MinGW 만한게 없더군요.
아무튼 위 방법을 토대로 OS 안가리는 C# 과 함께 돌아가는 표준 windows DLL 을 써서, 까다롭기 서울역에 거지 없는 C# 에서 좀 더 복잡하고 다양한 기능을 사용할 수 있게 됩니다.
개인적인 ... 정말 개인적인 의견으로는 .. 이왕이면 WPF 가 C++ 이나 C 로도 쓸 수 있었으면 좋겠네요...
가끔 Delphi 에서 WPF 를 쓸 수 있으면 대박이겠다는 생각이 듭니다... 아마 C# 은 따라오지 못하는 경지에까지 이를듯 할텐데 말이죠 ...