各位乡亲父老,欢迎大家来捧场!江湖卖艺,生活不易!技艺交流(投稿、打广告、链接交换),请搓这里

  VB创建的COM DLL和普通DLL区别

2019/11/8 17:49:07管理员 2190
- N +

VB调用API函数的实质是这样的:
在EXE中建立函数表,再调用MSVBVM6.DLL的DllFunctionCall,而在MSVBVM6.DLL中则是用LoadLibrary与GetProcessAddress得到API的入口地址,再把从EXE传入的参数压入堆栈,再用Call指令转入API的入口地址。
所以,API声明的实质是提供函数的信息(文件名,函数名,参数)
我曾用DASM反汇编了自己写的程序,我发现它调用API函数方法像VC++与Delph等写的程序一样,直接这样:
push param1
push param2
push param...
call SetMenu(或其它函数名)
从上面可以看出,用我的那和方法,调用API函数的效率更加高,因为它省略了中间步骤DllFunctionCall...等一大堆的指令与模块转换...
所以,一切并不是像楼上的那位老兄说的那样,把函数所有代码(如果我没理解错的话)加入EXE,它仅仅把函数的入口地址加入EXE,当Windows的PE装载器加载EXE时,PE装载器会自动修正函数的入口地址,直到调用该函数时,直接调用该地址,不需重复装载函数入口地址。

已经找到一类模块可以不用声明API就可以使用并返回值

Option Explicit

Public Enum DECLSPEC
        eStdCall
        eCDecl
End Enum

'***********************************************
'* This class module use excelent solution from
'* http://www.vbdotcom.com/FreeCode.htm
'* how to implement assembly calls directly
'* into VB code.
'***********************************************

Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes As Long)
Private m_lParameters() As Long 'list of parameters
Private m_lpFn As Long 'address of function to call
Private m_abCode() As Byte 'buffer for assembly code
Private m_lCP As Long 'used to keep track of latest byte added to code
Private m_hLib As Long
Private m_CallType As DECLSPEC

Public Property Let LibraryName(ByVal sData As String)
      If m_hLib Then FreeLibrary m_hLib
      m_hLib = LoadLibrary(sData)
      If m_hLib = 0 Then MsgBox "Can not find library " & Chr(34) & sData & Chr(34), vbCritical, "Function call error"
End Property

Public Property Let FunctionName(ByVal sData As String)
      Dim sMsg As String
      m_lpFn = GetProcAddress(m_hLib, sData)
      If m_lpFn = 0 Then
            sMsg = "Can not find function entry point for " & Chr(34) & sData & Chr(34)
            sMsg = sMsg & vbCrLf & "Note: function names are case sensitive, check out you function spelling!"
            MsgBox sMsg, vbCritical, "Function call error"
      End If
End Property

Public Property Let CallType(ByVal lData As DECLSPEC)
      m_CallType = lData
End Property

Public Function CallFunction(ParamArray FuncParams()) As Long
      Dim i As Long
      If m_lpFn = 0 Then
            MsgBox "Function not defined!", vbCritical, "Call function error"
            Exit Function
      End If
      ReDim m_abCode(0)
      ReDim m_lParameters(UBound(FuncParams) + 1)
      ReDim m_abCode(18 + 32 + 6 * UBound(m_lParameters))
      For i = 1 To UBound(m_lParameters)
            m_lParameters(i) = CLng(FuncParams(i - 1))
      Next i
      CallFunction = CallWindowProc(PrepareCode, 0, 0, 0, 0)
      m_lpFn = 0
End Function

Private Function PrepareCode() As Long
        Dim i As Long, codeStart As Long
        codeStart = GetAlignedCodeStart(VarPtr(m_abCode(0)))
        m_lCP = codeStart - VarPtr(m_abCode(0))
        For i = 0 To m_lCP - 1
                m_abCode(i) = &HCC
        Next
        PrepareStack
        For i = UBound(m_lParameters) To 1 Step -1
                AddByteToCode &H68 'push wwxxyyzz
                AddLongToCode m_lParameters(i)
        Next
        AddCallToCode m_lpFn
        If m_CallType = eCDecl Then ClearStack
        AddByteToCode &HC3
        AddByteToCode &HCC
        PrepareCode = codeStart
End Function

Private Sub AddCallToCode(ByVal dwAddress As Long)
        AddByteToCode &HE8
        AddLongToCode dwAddress - VarPtr(m_abCode(m_lCP)) - 4
End Sub

Private Sub AddLongToCode(ByVal lng As Long)
        Dim i As Integer
        Dim byt(3) As Byte
        CopyMemory byt(0), lng, 4
        For i = 0 To 3
                AddByteToCode byt(i)
        Next
End Sub

Private Sub AddByteToCode(ByVal byt As Byte)
        m_abCode(m_lCP) = byt
        m_lCP = m_lCP + 1
End Sub

Private Function GetAlignedCodeStart(ByVal dwAddress As Long) As Long
        GetAlignedCodeStart = dwAddress + (15 - (dwAddress - 1) Mod 16)
        If (15 - (dwAddress - 1) Mod 16) = 0 Then GetAlignedCodeStart = GetAlignedCodeStart + 16
End Function

Private Sub PrepareStack()
        AddByteToCode &H58 'pop eax -    pop return address
        AddByteToCode &H59 'pop ecx -    kill hwnd
        AddByteToCode &H59 'pop ecx -    kill wmsg
        AddByteToCode &H59 'pop ecx -    kill wParam
        AddByteToCode &H59 'pop ecx -    kill lParam
        AddByteToCode &H50 'push eax - put return address back
End Sub

Private Sub ClearStack()
      Dim i As Long
      For i = 1 To UBound(m_lParameters)
              AddByteToCode &H59 'pop ecx - remove params from stack
      Next
End Sub

Private Sub Class_Initialize()
      m_CallType = eStdCall
End Sub

Private Sub Class_Terminate()
      If m_hLib Then FreeLibrary m_hLib
End Sub


**********************************************************************
使用方法是
Dim FCall As cFuncCall

Private Sub Command1_Click()
Dim s() As Byte
s = StrConv("play c:\TESTSND.WAV", vbFromUnicode)
FCall.LibraryName = "winmm.dll"
FCall.FunctionName = "mciExecute"

FCall.CallFunction (VarPtr(s(0)))
End Sub

Private Sub Form_Load()
      Set FCall = New cFuncCall
End Sub

Private Sub Form_Unload(Cancel As Integer)
      Set FCall = Nothing
End Sub


不声明就用是不好的习惯.我想高级 VB 程序员只会偶尔为之,他们大多数情况下还是认真声明的.

因为它会避开 VB 的类型检查

而且,声明一下也不过是 粘贴一下而已

如果你能确保参数类型上不会冲突,不声明函数是可以的,不声明结构纯粹是自找麻烦,因为那需要你自己手工去算结构成员的位置.

com的dll和普通的dll很不一样.com的接口是通过UUID来标识的,而普通的dll一般是通过.def或者它的.h文件来声明的.

VC中DLL的创建及调用方法
此中只有实际才操作,而无相关理论

2            DLL的创建

首先,用VC集成开发界面中的“新建”,新建一个项目。无论是VC6.0还是VC.NET,都有建立DLL项目的选项。只不过有些稍有不同,例如VC.NET中就有ISAPI DLL,扩展存储过程DLL等,这些都不在讨论的范围。例如我们建立了一个用静态连接MFC库的DLL项目,名称为mydll

然后,编辑mydll.cpp文件,在其中加入我们自己的函数void go()。注意,不需要在mydll.h中声明它,而需要将函数头变成如下样子:

extern “c” __declspec(dllexport) void go()

{

//code……

}

dllexport表示这个函数是由外部调用的。

由于是否带参数,要影响到外部调用的方式,因此,我们再声明一个带参数的函数:

extern “c” __declspec(dllexport) void went(CString str)

{

//code……

}

OK,下面编译连接形成mydll.dll文件。

2            DLL的调用

好,下面我们就用VC写个程序调用它。在调用的函数中,首先要获得DLL的句柄,有如下语句:

HINSTANCE          dllinstance;

dllinstance=::LoadLibrary(strDllUrl);

if(dllinstance==NULL) AfxMessageBox("can't open dll file");

      其中strDllUrl是mydll.dll路径的字符串,这样程序才能找到它。::LoadLibrary获得参数标识的DLL文件的句柄。

        获得句柄后,下面要获得函数地址以便执行它。有如下语句:

        FARPROC    proc;

        proc=GetProcAddress(dllinstance,"go");

          if(proc==NULL) AfxMessageBox("can't find function");

          else proc();

FARPROC是一个远程过程指针,通过GetProcAddress获得函数的地址。它的两个参数就是dll文件句柄和函数的名字了。

然后FARPROC就可以和go一样的使用了,它就是go ,go 就是它。

而对于带参数的DLL中的函数,调用方法有所不同。因为对函数的调用是通过对它地址的引用进行的,这样,传入参数对不对,在函数调用程序的编译和联接过程中,无法知道其正确性。因此,要在调用程序中对DLL中带参数的函数做个声明,如mydll中的went,我们要做个声明如下:

typedef void (FAR __cdecl *MYWENT)(CString);

然后以类型MYWENT声明变量既可调用,如下:

        MYWENT myproc;

        myproc =(MYWENT)GetProcAddress(dllinstance,"go");

          if(myproc ==NULL) AfxMessageBox("can't find function");

          else myproc (“o-----yeah---------”);

注意声明的时候呢,由于DLL中WENT的定义为C语言调用规范,因此MYWENT前一定要用__cdecl,而VC中常用的__stdcall是PASCAL调用规范,不可以的。一定要注意。




VB中创建的DLL只是COM组件,无法作为输出函数的DLL,其实这只是个错误的说法。其实MS非常狡猾,如果你是个VB疯狂发烧友的话,应该早就狂试出这种可以创建输出函数的DLL的方法。
          VB编译文件实际上采取了两次编译的方法,首先是调用C2.exe产生*.OBJ文件,然后调用Link.EXE连接。如果在LINK的时候添加EXPORT选项,实际上是可以输出函数的。但是,在VB的工程选项中将这些屏蔽了。而且过分的是:VB在Build完成后会将OBJ文件删除,这样就无法手动通过Link来创建我们需要的DLL了。不过我找到一个比较龌鹾的变通的方法,就是先创建一个Exe工程,在Form_Load事件里面写下面的语句:
          
        Sub Main
          If MsgBox("哈哈", vbOKCancel) = vbOK Then
          Shell "link2.exe " & Command$
          End If
        End Sub
          
        然后编译为LinkTemp.EXE,接下来将LINK.EXE改名为Link2.exe,将LinkTemp.EXE改名为Link.EXE。这样在VB调用Link.EXE时会弹出对话框,处理就会中断。这时就可以有机会将OBJ文件拷贝出来了。
          然后我创建了一个ActiveX DLL工程,在这个工程里面添加一个Module并创建一个Public函数mathadd:
          
        Public Function mathadd(ByVal a As Long, ByVal b As Long) As Long
          mathadd = a + b
        End Function
          
          编译这个工程,在Link的时候就会中断。然后把创建的Class1.obj、Module1.obj、Project1.obj备份出来。
          然后就可以调用Link2.exe连接OBJ到DLL了,我的连接代码是:
          
        Link2.exe "e:\vbdll\Class1.obj" "e:\vbdll\Module1.obj" "e:\vbdll\Project1.obj" "E:\Program Files\Microsoft Visual Studio\VB98\VBAEXE6.LIB" /ENTRY:__vbaS /EXPORT:mathadd /OUT:"e:\vbdll\ProjectOK.dll" /BASE:0x11000000 /SUBSYSTEM:WINDOWS,4.0 /VERS
          
          注意里面的/ENTRY和/EXPORT开关,/EXPORT开关声明了输出函数mathadd。这样就大功告成了,可以被其他语言引入,例如在VB中,只需要:
          
        Private Declare Function mathadd Lib "e:\vbdll\ProjectOK.dll" (ByVal a As Long, ByVal b As Long) As Long    


LINK这个方法我用过,是能执行DllMain里的代码。
但是有个问题就是只有VB的程序调用你的这个DLL,你DLL的DLLMAIN里的代码才会运行。要是是VC调用你这个DLL,那么DLLMAIN里的代码就不运行了。
我是想做个线程注入的东西,把DLL文件注入到某个进程里我已经做好了,关键的就是这个DLL插入以后不能由DLLMAIN启动。所以这个DLL无法运行。
要是是用VC写这个DLL的话就支持DLLMAIN了,但是自己对VC不怎么了解,想把代码改成VC真的好难!
请教大家能否有什么好点的方法可以实现



我知道VC可以,但是自己并不怎么熟悉VC。
我想请教各位高手VB能否写WIN32的DLL文件呢?
这个DLL必须有Public Function DllMain(hInst As Long, fdwReason As Long,
    lpvReserved As Long) As Boolean的    





VB写标准DLL
VB中创建的DLL只是COM组件,无法作为输出函数的DLL,其实这只是个错误的说法。其实MS非常狡猾,如果你是个VB疯狂发烧友的话,应该早就狂试出这种可以创建输出函数的DLL的方法。
          VB编译文件实际上采取了两次编译的方法,首先是调用C2.exe产生*.OBJ文件,然后调用Link.EXE连接。如果在LINK的时候添加EXPORT选项,实际上是可以输出函数的。但是,在VB的工程选项中将这些屏蔽了。而且过分的是:VB在Build完成后会将OBJ文件删除,这样就无法手动通过Link来创建我们需要的DLL了。不过我找到一个比较龌鹾的变通的方法,就是先创建一个Exe工程,在Form_Load事件里面写下面的语句:
          
        Sub Main
          If MsgBox("哈哈", vbOKCancel) = vbOK Then
          Shell "link2.exe " & Command$
          End If
        End Sub
          
        然后编译为LinkTemp.EXE,接下来将LINK.EXE改名为Link2.exe,将LinkTemp.EXE改名为Link.EXE。这样在VB调用Link.EXE时会弹出对话框,处理就会中断。这时就可以有机会将OBJ文件拷贝出来了。
          然后我创建了一个ActiveX DLL工程,在这个工程里面添加一个Module并创建一个Public函数mathadd:
          
        Public Function mathadd(ByVal a As Long, ByVal b As Long) As Long
          mathadd = a + b
        End Function
          
          编译这个工程,在Link的时候就会中断。然后把创建的Class1.obj、Module1.obj、Project1.obj备份出来。
          然后就可以调用Link2.exe连接OBJ到DLL了,我的连接代码是:
          
        Link2.exe "e:\vbdll\Class1.obj" "e:\vbdll\Module1.obj" "e:\vbdll\Project1.obj" "E:\Program Files\Microsoft Visual Studio\VB98\VBAEXE6.LIB" /ENTRY:__vbaS /EXPORT:mathadd /OUT:"e:\vbdll\ProjectOK.dll" /BASE:0x11000000 /SUBSYSTEM:WINDOWS,4.0 /VERS
          
          注意里面的/ENTRY和/EXPORT开关,/EXPORT开关声明了输出函数mathadd。这样就大功告成了,可以被其他语言引入,例如在VB中,只需要:
          
        Private Declare Function mathadd Lib "e:\vbdll\ProjectOK.dll" (ByVal a As Long, ByVal b As Long) As Long

0人赞 分享 二维码 赏一个
选择分享方式
移步手机端
文章手机二维码

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章
选择打赏方式
微信赞助

打赏