본문 바로가기

Developement

C# 에서 DLL 로드 동적으로 하기.

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# 은 따라오지 못하는 경지에까지 이를듯 할텐데 말이죠 ...