Undocument Windows API và VBA

Liên hệ QC

ThangCuAnh

Mới rờ Ét xeo
Tham gia
1/12/17
Bài viết
896
Được thích
792
Giới tính
Nam
Nghề nghiệp
Coder nghỉ hưu, RCE dạo
Cái laptop hư lâu lắc, phải gởi vào thành hồ chứa mưa sữa, mới lấy về.
Nên quay lại tiếp với cái gọi là "rờ chxx em" Windows và VBA.
Topic này tui sẽ đăng lần lượt những gì cu anh tui phát hiện ra trong quá trình "rờ em" Windows API, DLLs và VBAxxx.dll. Các tips, tricks này sẽ bảo đảm không có trên ông "Gấu gồ". Và dùng được cho VBA trên Offices. Chứ "ưn đồ cú mèn" API mà chỉ dùng được cho C/C++, Delphi... thì dân tin học VP ở đây thua.
Tui chỉ sẽ tập trung ở các Windows DLL sau: kernel32.dll, shell32.dll, shlwapi.dll, oleaut32.dll, ole32.dll, advapi32.dll... và 1 ít từ ntdll.dll (core usermode API của Windows). Trên VBExxx.dll thì tui chỉ tập trung vào VBE6 của Office 2007, VBE7 của Office2010 32 và 64bit, các VBExxx.dll khác cũng sẽ gần như tương đượng, không khác nhau mấy.
 
Lần chỉnh sửa cuối:
À, máy befaint Office64 à, code mình chỉ mới test trên Office 32. Để mở VBE 64 xem giới hạn nó là bao nhiêu.
Bài đã được tự động gộp:

Office 2016 x64, VBE7.dll là v7.1.10.77 phải không @befaint ?
 
Upvote 0
Win10 phải không @befaint ? Befaint vào source mdlTest, chỉnh const MAX_LEN tăng lên 1 hay 2 rồi chạy lại thử xem ?
 
Upvote 0
Befaint và các bạn chịu khó xem kỹ cái hình mình mới post, sẽ thấy giới hạn 0x3FFFFFFF. Đọc code mình kỹ sẽ hiểu ý mình muốn nói
Bài đã được tự động gộp:

Số 5 với số 14 truyền vào hàm EbRaiseExceptionCode chính là Err. Number 5 và 14 đó
 
Lần chỉnh sửa cuối:
Upvote 0
Em test file của bác trên Excel 32-bit thì 2 lệnh gọi test SysAllowStringLen đều bị lỗi "Overflow".
Nếu tôi không đọc nhầm thì đâu có test SysAllowStringLen. Chỉ có Test_rtcSpaceBstr thôi. Mà nếu có lỗi thì đâu có khẳng định được là lỗi của SysAllowStringLen?

Bạn thử chạy SysAllowStringLen trong Delphi xem. Tôi tin là sẽ không có lỗi thậm chí với MaxInt = $7FFFFFFF = 2 147 483 647
 
Upvote 0
Đi tắm chợt nảy ra ý tưởng về vụ TypeName của bạn Tuân. Khỏi viết hàm Delphi chi cho mệt, dùng luôn hàm VBA rtcTypeName đã được export từ VBE6/7.dll. Prototype hàm đã có rồi đó, nhận pointer to VARIANT, trả về BSTR string.
VBExxx.dll chắc chắn đã load 100% vào Excel, bác chỉ cần GetProcAddress, call là xong.
Tương tự với các hàm export, undocument khác từ VBExxx.dll.
Sure là 100% chạy được, đúng.
Để mai mình làm file Excel VBA test cho.
 
Lần chỉnh sửa cuối:
Upvote 0
Nếu tôi không đọc nhầm thì đâu có test SysAllowStringLen. Chỉ có Test_rtcSpaceBstr thôi. Mà nếu có lỗi thì đâu có khẳng định được là lỗi của SysAllowStringLen?

Bạn thử chạy SysAllowStringLen trong Delphi xem. Tôi tin là sẽ không có lỗi thậm chí với MaxInt = $7FFFFFFF = 2 147 483 647

Ah, em chỉ test Test_rtcSpaceBstr thôi chứ không test cái còn lại. Như bác Cu Anh nói thì trong hàm Space$ dùng hàm SysAllowStringLen nên kết luận thông qua Space$. Cái này em cứ test theo bác chủ topic để bác ấy nghiên cứu tiếp.

Em đã test hàm SysAllowStringLen trong Delphi với con số lớn hơn 2^31 không có lỗi gì. Có lẽ tài liệu của MS nói về String Type là có quan hệ với khả năng lưu trữ ký tự trên Cell.Value của Excel?
Bài đã được tự động gộp:

Đi tắm chợt nảy ra ý tưởng về vụ TypeName của bạn Tuân. Khỏi viết hàm Delphi chi cho mệt, dùng luôn hàm VBA rtcTypeName đã được export từ VBE6/7.dll. Prototype hàm đã có rồi đó, nhận pointer to VARIANT, trả về BSTR string.
VBExxx.dll chắc chắn đã load 100% vào Excel, bác chỉ cần GetProcAddress, call là xong.
Tương tự với các hàm export, undocument khác từ VBExxx.dll.
Sure là 100% chạy được, đúng.
Để mai mình làm file Excel VBA test cho.

Em cũng tính dùng cái hàm họ đã exports để dùng luôn, khi nào có time thì xem cách lấy từ IDispacth. Tuy nhiên bác kiểm tra kỹ cho em thêm hàm rtcTypeName nó export theo stdcall hay safecall?
 
Upvote 0
Bạn Tuân test trên Win 64 hay 32,app 32 hay 64.
Trên Win64 thì memory cho user khỏi phải lo. befaint chạy Ok với 0x3FFFFFFF đó
Mình đang Re, test 32.
Tất cả hàm API đều là stdcall hết bạn. Quy ước rồi. COM Object methods mới safecall.
 
Upvote 0
Bạn Tuân test trên Win 64 hay 32,app 32 hay 64.
Trên Win64 thì memory cho user khỏi phải lo. befaint chạy Ok với 0x3FFFFFFF đó
Mình đang Re, test 32.

Em test trên Excel 64-bit, Win10 64-bit. Vụ này em nghi có sự can thiệp của Excel vì nó chỉ được cấp phát bộ nhớ giới hạn trong Windows. Tùy vào trạng thái bộ nhớ của Excel còn được phép bao nhiêu mà nó cho phép một vài hàm nào đó mở rộng vùng nhớ.

Tất cả hàm API đều là stdcall hết bạn. Quy ước rồi. COM Object methods mới safecall.

Nếu bác tự "Declare Function rtcTypeName ..." trong VBA chạy được thì chắc chắn là export với "stdcall" nếu không em nghi là "safecall". "SafeCall" dùng trong COM nhưng cũng dùng để reference trong TLB và những hàm đó sẽ export dạng safecall (vụ này em tự viết các hàm API riêng theo cả hai mẫu export).
Bài đã được tự động gộp:

Thông báo với bác CÙ Anh. Em đã kiểm tra và khai báo API với hàm rtcTypeName ok cho cả VBA6.dll và VBA7.DLL rồi nhé. Chính xác rtcTypeName đã được export theo stdcall. :)

Mã:
Option Explicit
#If VBA7 Then
Declare PtrSafe Function rtcTypeName Lib "VBE7.DLL" (v As Variant) As String
#Else
Declare Function rtcTypeName Lib "VBE6.DLL" (v As Variant) As String
#End If

Sub TestTypeNameAPI()
    Dim sName As String
    sName = rtcTypeName(ActiveCell)
    MsgBox StrConv(sName, vbFromUnicode)
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Ox3FFFFFFF là giới hạn cho các hàm VBA nhận vào tham số là số ký tự rồi. Ms coder code cái VBExxx.dll đã code, quy định constant này, tới bản 2019 64bit của befaint vừa up cũng vậy.
Mình up hình rành rành và code comment vậy mà các bạn kg xem kỹ sao trời.
Nói lại cho rõ , trong hàm Space và Space$, nếu số ký tự yêu cầu lớn hơn 0x3FFFFFFF, lỗi sẽ được raise và Err.Number = 5.
Nếu nhỏ hơn, call SysAllocStringLen. Nếu trả về NULL thì raise error với Err.Number là 14.
Kg liên quan gì tới Excel ở đây, đâu dùng VBA cũng vậy. Chỉ là oleaut32.dll và kernel.dll, xuống tới kernel Windows và virtual memory manager.
 
Lần chỉnh sửa cuối:
Upvote 0
Chúc mừng bác. Thấy sự lợi hại của undocument API với "rờ cxx em chua
 
Upvote 0
Ox3FFFFFFF là giới hạn cho các hàm VBA nhận vào tham số là số ký tự rồi. Ms coder code cái VBExxx.dll đã code, quy định constant này, tới bản 2019 64bit của befaint vừa up cũng vậy.
Mình up hình rành rành và code comment vậy mà các bạn kg xem kỹ sao trời.
Nói lại cho rõ , trong hàm Space và Space$, nếu số ký tự yêu cầu lớn hơn 0x3FFFFFFF, lỗi sẽ được raise và Err.Number = 5.
Nếu nhỏ hơn, call SysAllocStringLen. Nếu trả về NULL thì raise error với Err.Number là 14.
Kg liên quan gì tới Excel ở đây, đâu dùng VBA cũng vậy. Chỉ là oleaut32.dll và kernel.dll, xuống tới kernel Windows và virtual memory manager.

Em quên không nhìn cái hình bác chụp, nó đã code rõ vấn đề giới hạn. Em cũng đã đoán thư viện VBA dll tự giới hạn phạm vi chứ không phải hàm SysAllowStringLen của Windows. :).

Bác tiếp tục tìm các hàm undocument nhé. EM đang đưa cái hàm rtcTypeName vào trong Delphi thử nhưng chưa thành công, hàm nó trả về '' :)
 
Upvote 0
Em thông báo lại công việc khai thác như sau

Trong VBA khai báo API đã thành công:
Mã:
Option Explicit
#If VBA7 Then
Declare PtrSafe Function rtcTypeName Lib "VBE7.DLL" (v As Variant) As String
#Else
Declare Function rtcTypeName Lib "VBE6.DLL" (v As Variant) As String
#End If

Sub TestTypeNameAPI()
    Dim sName As String
    sName = rtcTypeName(ActiveCell)
    MsgBox StrConv(sName, vbFromUnicode)
End Sub

Trong Delphi thì hàm trả về ''. Các lệnh gọi API tới hàm rtcTypeName đều tốt, nhưng không hiểu sao hàm lại trả về '' .
Mã:
program Test_VBA_API.dproj;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Winapi.Windows,
  ComObj,
  System.Variants;

type
  TFuncAVBAPI_rtcTypeName = function(v: OleVariant): WideString; stdcall;

var sName: WideString;
  h: HMODULE;
  pFunc: TFuncAVBAPI_rtcTypeName;
  App: OleVariant;
begin
  try
    App := GetActiveOleObject('Excel.Application');
    Writeln('Ung dung da ma la', ': ', App.Name);
    h := LoadLibrary('C:\Program Files (x86)\Common Files\Microsoft Shared\VBA\VBA7\VBE7.DLL');
    try
      pFunc := GetProcAddress(h, 'rtcTypeName');
      sName := TFuncAVBAPI_rtcTypeName(pFunc)(App.ActiveCell);
      Writeln('Gia tri bien la', ': ', sName);
    finally
      FreeLibrary(h);
      App := Unassigned;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  Readln;

end.
 
Upvote 0
Prototype nó ghi rõ, và mình cũng nói rồi mà bạn. Pointer to struct VARIANT, tức trên Delphi là @ biển kiểu Variant hay TVarData. Khai báo Delphi sửa v thành v: Pointer.
Ông VBA và Delphi, ông nào cũng lanh chanh chuyển kiểu ngầm hết, khổ.
 
Lần chỉnh sửa cuối:
Upvote 0
Prorotype nó ghi rõ, và mình cũng nói rồi mà bạn. Pointer to strucr VARIANT, tức trên Delphi là @ biển kiểu Variant hay TVarData. Khai báo Delphi sửa v thành v: Pointer.
Ông VBA và Delphi, ông nào cũng lanh chanh chuyển kiểu ngầm hết, khổ.

Thành công trên Delphi rồi bác nhé. Em quên nguyên tắc con trỏ khi khai báo hàm này :).
 
Upvote 0
Các bạn có ai thắc mắc là trong code VBA của bác Tuân, v as Variant, còn trong Delphi v: Pointer không nhỉ ?
Chuyện dài dòng đấy :p
 
Upvote 0
Các bạn có ai thắc mắc là trong code VBA của bác Tuân, v as Variant, còn trong Delphi v: Pointer không nhỉ ?
Chuyện dài dòng đấy :p
Có đấy tính hỏi trong Dephi viết sao cho nó hiểu đây .... và ứng dụng nó như thế nào là hiệu quả nhất (khác biệt nhất ) hay cũng như Msgbox Typename(x) như lâu nay vẫn làm thế trên VBA
 
Lần chỉnh sửa cuối:
Upvote 0
Bạn @kieu manh hỏi bác @Nguyễn Duy Tuân ấy, dùng ra sao !?
Tiếp tục với hàm unsigned int __stdcall StrLenChk(unsigned int nLen) này, hay tức là hằng 0x3FFFFFFF (1073741823) này.
Trong các VBExxx.dll 32bit, hàm này là hàm riêng, còn trên các VBExxx.dll 64bit, hàm này được inline compiled vào trực tiếp hàm caller.
Có rất nhiều các hàm VBA đều phải dùng hàm StrLenChk này. Ví dụ các hàm sau:
1. String$ = rtcStringBstr
2. String = rctStringVar
3. InStr với param truyền vào là kiểu string = _vbaInStr
4. InStr với param truyền vào là kiểu Variant = __vbaInStrVar
5. 1 đống các hàm InStr với tham số truyền vào là các kiểu khác như Char, __vbaInStrVarB
6. Hàm VBA InStr gốc ở ngoài = rtcInStr
7. Các VBA Input statement
8. Các hàm VBA Mid, Mid$, statement Mid, Mid$
....
Và rất rất nhiều nữa, các hàm A gọi hàm B, B gọi C, C gọi StrLenChk, đều văng Err.Number 5 lên hết.
Nên thôi, viết ra quá dài dòng, nhiều không xuể, nên các bác cứ nhớ giúp cu anh tui là với VBA, cái nào mà đòi hỏi, nhận tham số là số ký tự hay truyền vào Len string thì luôn luôn phải nhỏ hơn 0x3FFF FFFFF (&H3FFF FFFF: VBA, $3FFFF FFFF: Delphi, hay 1073741823 decimal). Vậy thôi. Coder MS code cái VBA DLLs nó đã quy định như vậy rồi.

Hì hì, mà nó quy định vậy là đúng đó, không phải sai hay document của MS nó sai đâu. Đố ai tìm ra được tại sao có cái hằng 0x3FFFFFFF này ???? Tui cũng chợt nhớ ra tối qua thôi, lúc tắm :)

@Nguyễn Duy Tuân: nick tui không phải là Cù Anh, hay Cù Anh Thắng mà hồi xưa nhiều báo đăng, nó đọc bình dân, nhà quê là Thằng Cu Anh thôi :p
Với lại nếu bác mang code rtcTypeName cho Delphi vào Addin Tools của bác, bác check xem thử là ở những điều kiện nào VBE6/7.dll không được Excel load lên, như disable Macro, disable ActiveX... Mà tui cũng nghĩ, cái AddIn Tools của bác mà lên được thì 100% chắc chắn VBExxx.dll đã load lên rồi :)
Mà nếu nó đã được load lên rồi, thì chỉ cần GetProcAddress(GetModuleHandle('VBEx.dll'), 'rtcTypeName')) thôi, chả cần đường dẫn gì cho phức tạp.
 
Lần chỉnh sửa cuối:
Upvote 0
Web KT
Back
Top Bottom