AI muốn lập trình DLL cho Excel và các loại bằng Delphi thì xem video này nhé!

Liên hệ QC

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,737
Được thích
10,243
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
Lần chỉnh sửa cuối:
Mạnh Copy y trang Code bài 811 và làm như hình sau:
Capture.JPG
 
Upvote 0
Trong code project có đoạn {$ *.Res} tạm comment lại nhé
 
Upvote 0
Đọc kỹ hdsd trước khi dùng :)
 
Upvote 0
Vâng. Hàm InStr có thể dùng bởi hàm Pos hoặc PosEx cảu Delphi rất nhanh, code nguồn Delphi hàm Pos() viết bằng Assemble rất ngọt. Tuy nhiên hàm InStrRev Delphi không viết giúp, mình phải tự viết và dùng vòng lặp và duyệt PChar nên cảm thấy chưa ưng. Nếu lấy được InStrRev em sẽ test để so sánh tốc độ với Delphi. Khi viết Delphi cho Office thì cứ thằng nào ngon nhất ta dùng. Ý em là vậy đó bác :).
Hihi cái dụ này mấy bữa em mò client sever có đụng tới nó đó anh
 
Upvote 0
Chính xác chạy rất tốt xong thấy rất nhiều code
View attachment 222173
Lỗi {$ *.Res} không phải là lỗi nghiêm trọng.

Khi bạn tạo project mới thì Delphi tự thêm {$R *.RES}. Bạn chon Save All rồi đóng Delphi. Trên đĩa sẽ có project.res. Nếu bạn xóa project.res rồi mở lại project.dpr thì Delphi sẽ thông báo là không tìm thấy project.res. Nó tìm vì trong unit project.dpr có {$R *.RES}. Và nó hỏi có tạo lại project.res không. Chỉ cần gật đầu là xong. Đây không phải là lỗi nghiêm trọng.
-------
Bạn có nhìn thấy thông báo "CoInitialize has not been called" không?

Thực ra tôi chỉ cài Delphi 5 vì máy 18 tuổi, C có 10 GB, XP Home. Bạn hãy thử như sau.
Trước dòng
Mã:
App := GetActiveOleObject('Excel.Application');
thì thêm dòng
Mã:
if InitProc <> nil then TProcedure(InitProc);
------
Mà chỉ thêm như sau cũng được
Mã:
TProcedure(InitProc);

vì trong ComObj có
Mã:
initialization
begin
  ...
  if not IsLibrary then
  begin
    SaveInitProc := InitProc;
    InitProc := @InitComObj;
  end;
end;

Tức InitProc <> nil (InitProc := @InitComObj)

Tức sẽ thực hiện InitComObj
Mã:
procedure InitComObj;
begin
  if SaveInitProc <> nil then TProcedure(SaveInitProc);
  if (CoInitFlags <> -1) and Assigned(ComObj.CoInitializeEx) then
  begin
    NeedToUninitialize := Succeeded(ComObj.CoInitializeEx(nil, CoInitFlags));
    IsMultiThread := IsMultiThread or
      ((CoInitFlags and COINIT_APARTMENTTHREADED) <> 0) or
      (CoInitFlags = COINIT_MULTITHREADED);  // this flag has value zero
  end
  else
    NeedToUninitialize := Succeeded(CoInitialize(nil));
end;

Trường hợp này (nếu tôi không lầm thì trong trường hợp đang nói thì CoInitFlags =-1
Mã:
var
  CoInitFlags: Integer = -1;  // defaults to no threading model, call CoInitialize()
) sẽđược thực hiện
Mã:
NeedToUninitialize := Succeeded(CoInitialize(nil));
 
Lần chỉnh sửa cuối:
Upvote 0
Lỗi {$ *.Res} không phải là lỗi nghiêm trọng.

Khi bạn tạo project mới thì Delphi tự thêm {$R *.RES}. Bạn chon Save All rồi đóng Delphi. Trên đĩa sẽ có project.res. Nếu bạn xóa project.res rồi mở lại project.dpr thì Delphi sẽ thông báo là không tìm thấy project.res. Nó tìm vì trong unit project.dpr có {$R *.RES}. Và nó hỏi có tạo lại project.res không. Chỉ cần gật đầu là xong. Đây không phải là lỗi nghiêm trọng.
-------
Bạn có nhìn thấy thông báo "CoInitialize has not been called" không?

Thực ra tôi chỉ cài Delphi 5 vì máy 18 tuổi, C có 10 GB, XP Home. Bạn hãy thử như sau.
Trước dòng
Mã:
App := GetActiveOleObject('Excel.Application');
thì thêm dòng
Mã:
if InitProc <> nil then TProcedure(InitProc);
------
Mà chỉ thêm như sau cũng được
Mã:
TProcedure(InitProc);

vì trong ComObj có
Mã:
initialization
begin
  ...
  if not IsLibrary then
  begin
    SaveInitProc := InitProc;
    InitProc := @InitComObj;
  end;
end;

Tức InitProc <> nil (InitProc := @InitComObj)

Tức sẽ thực hiện InitComObj
Mã:
procedure InitComObj;
begin
  if SaveInitProc <> nil then TProcedure(SaveInitProc);
  if (CoInitFlags <> -1) and Assigned(ComObj.CoInitializeEx) then
  begin
    NeedToUninitialize := Succeeded(ComObj.CoInitializeEx(nil, CoInitFlags));
    IsMultiThread := IsMultiThread or
      ((CoInitFlags and COINIT_APARTMENTTHREADED) <> 0) or
      (CoInitFlags = COINIT_MULTITHREADED);  // this flag has value zero
  end
  else
    NeedToUninitialize := Succeeded(CoInitialize(nil));
end;

Trường hợp này (nếu tôi không lầm thì trong trường hợp đang nói thì CoInitFlags =-1
Mã:
var
  CoInitFlags: Integer = -1;  // defaults to no threading model, call CoInitialize()
) sẽđược thực hiện
Mã:
NeedToUninitialize := Succeeded(CoInitialize(nil));
Em chụp cái hình nó như sau
Em đang suy nghĩ tới lui mà chưa tìm ra cái giá trị đặc biệt khi ứng dụng nó viết trên Delphi

"2. Cách khai báo API hàm rtcTypeName() để dùng trong Delphi. Nếu chúng ta tạo Add-in thì đây là tư liệu quý."

chắc tại trình độ của em chưa thể nhìn ra hết khi nào hội đủ kiến thức may nhìn ra !!???
CoInitialize.JPG
 
Lần chỉnh sửa cuối:
Upvote 0
Các cậu làm tớ chán rồi, ứ thèm post nữa :p
 
Upvote 0
Em chụp cái hình nó như sau
Em đang suy nghĩ tới lui mà chưa tìm ra cái giá trị đặc biệt khi ứng dụng nó viết trên Delphi
Không hiểu ý bạn

Bạn đã có
Mã:
if InitProc <> nil then TProcedure(InitProc);
App := GetActiveOleObject('Excel.Application');

chưa?
 
Upvote 0
Khổ bạn Mạnh ghê, mình nói học Debug trong IDE đi mà không chịu học, chỉ đông chỉ tây, làm nam làm bắc hết :p
 
Upvote 0
Hình này là sau khi thêm 2 dòng đó Anh
Tôi hỏi vì hình trước ở bài #827 của bạn có "EOleSysError: CoInitialize has not been called"

Bây giờ mới có "EOleSysErro: Operation unavailable"

Bạn đã đính hình rất cẩu thả.

2 lỗi này là khác nhau. Như vậy sau khi thêm if InitProc <> nil then TProcedure(InitProc); thì không còn lỗi 1 nữa.


Về lỗi thứ 2 thì bạn có khởi động Excel trước khi chạy code Delphi không? Nếu không thì có lỗi tại dòng

App := GetActiveOleObject('Excel.Application');

Làm gì có server Excel nào đang hoạt động mà đòi Get?

Tôi không tin là Tuân không gặp lỗi. Vì Tuân có thể cho code không hẳn như có trên đĩa (vụ vbTextCompare), không hẳn là đúng thực tế, nên khó đoán. Nếu là code đưa trên GPE thì nhất định có lỗi 1.

Debug thì thấy lỗi "EOleSysError: CoInitialize has not been called" nhưng trên cơ sở "Đọc kỹ hdsd trước khi dùng" thì có đến tết Công Gô kieu manh cũng không đoán được phải thêm cái gì. "Đọc kỹ hdsd trước khi dùng" là chung chung chả nói lên cái gì cụ thể.
 
Lần chỉnh sửa cuối:
Upvote 0
Bác Tuân đã post rõ ràng cách chạy code rồi, bạn Mạnh nhà ta xớn xa xớn xác thôi, nên tui mới ghi đọc kỹ hdsd trước khi dùng, kkk :p
 
Upvote 0
Tôi hỏi vì hình trước ở bài #827 của bạn có "EOleSysError: CoInitialize has not been called"

Bây giờ mới có "EOleSysErro: Operation unavailable"

Bạn đã đính hình rất cẩu thả.

2 lỗi này là khác nhau. Như vậy sau khi thêm if InitProc <> nil then TProcedure(InitProc); thì không còn lỗi 1 nữa.


Về lỗi thứ 2 thì bạn có khởi động Excel trước khi chạy code Delphi không? Nếu không thì có lỗi tại dòng

App := GetActiveOleObject('Excel.Application');

Làm gì có server Excel nào đang hoạt động mà đòi Get?

Tôi không tin là Tuân không gặp lỗi. Vì Tuân có thể cho code không hẳn như có trên đĩa (vụ vbTextCompare), không hẳn là đúng thực tế, nên khó đoán. Nếu là code đưa trên GPE thì nhất định có lỗi 1.

Debug thì thấy lỗi "EOleSysError: CoInitialize has not been called" nhưng trên cơ sở "Đọc kỹ hdsd trước khi dùng" thì có đến tết Công Gô kieu manh cũng không đoán được phải thêm cái gì. "Đọc kỹ hdsd trước khi dùng" là chung chung chả nói lên cái gì cụ thể.

Cái dòng màu đem như dân quãng ninh kia nhột lắm Anh ... Em mò học mà cứ quăng thế tết tây quá !!!
Mạnh Phán xanh dì 1 câu trên cung trăng có đầy vàng mi cứ lên đó mà lấy ........... vãi kinh hồn nuôn !!! /*-*/ -0-0-0-
 
Upvote 0
Tuân chỉ viết
Chạy Excel với quyền "Run as administrator"
kieu manh không làm đúng thì sẽ có lỗi "EOleSysError: Operation unavailable". Chỉ cần "Đọc kỹ hdsd trước khi dùng" một lần nữa thì sẽ hết lỗi này.

Nhưng còn lỗi khó hơn là "EOleSysError: CoInitialize has not been called" mà chỉ trên cơ sở "Đọc kỹ hdsd trước khi dùng" thì có đọc đến tết Công Gô kieu manh vẫn không biết phải sửa gì, thêm gì.
Bài đã được tự động gộp:

Cái dòng màu đem như dân quãng ninh kia nhột lắm Anh ... Em mò học mà cứ quăng thế tết tây quá !!!
Mạnh Phán xanh dì 1 câu trên cung trăng có đầy vàng mi cứ lên đó mà lấy ........... vãi kinh hồn nuôn !!! /*-*/ -0-0-0-
Cuối cùng cho tới bây giờ bạn đã làm được chưa?
 
Upvote 0
thôi tạm dừng đó Anh ... lúc nào Em coi lại các bước sau
Em vẫn làm theo hướng dẫn bài 811 mà ... Chạy Excel Run As
mmm.png
 
Upvote 0
Bạn @kieu manh đang làm theo hàm rctTypeName chứ không phải hàm InStrRev anh @batman1 a. Code cả project em để full không che ấy. EM chạy không lỗi gì.

Trong VBA có hàm TypeName() để lấy tên của class hay tên của lớp khai báo control hay các object. Khi các bạn lập trình Delphi cho Excel muốn dùng hàm TypeName không thể được vì nó không cùng môi trường VBA. Giải pháp chúng ta phải biết hàm API nào trong thư viên VBAxxx xuất nó ra đồng thời phải biết tham số để sử dụng. Chúng ta thực sự khó nếu không có tài liệu cung cấp từ nhà lập trình ra vbaxxx.

Nhân chủ đề Undocument API VBA ở bài số #10 tại đây

Code mà bác CU Anh moi ra nó là C và rất phức tạp đây:

Mã:
VARIANT *__stdcall rtcTypeName(VARIANT *pVar)
{
    int vt; // edi@1
    int (__stdcall ***v2)(_DWORD, _DWORD, _DWORD); // esi@9
    const unsigned __int16 *v3; // ebx@11
    VARIANT *result; // eax@14
    unsigned int v5; // esi@18
    int v6; // eax@31
    int v7; // esi@34
    int v8; // eax@34
    int v9; // esi@38
    VARIANT *v10; // esi@40
    LONG v11; // eax@53
    LONG v12; // eax@55
    int v13; // eax@56
    int v14; // eax@57
    int v15; // [sp+Ch] [bp-14h]@35
    int v16; // [sp+10h] [bp-10h]@33
    unsigned int bCurrency; // [sp+14h] [bp-Ch]@1
    int v18; // [sp+18h] [bp-8h]@34
    BSTR bstrString; // [sp+1Ch] [bp-4h]@1

    vt = pVar->vt & 0x9FFF;
    bCurrency = (pVar->vt >> 13) & 1;
    bstrString = 0;
    if ( vt > VT_UNKNOWN )
    {
        if ( vt == VT_DECIMAL || vt == VT_UI1 )
        {
            goto DecimalOrUI1;
        }
        if ( vt != VT_RECORD )
        {
            goto LABEL_44;
        }
        if ( dword_65246024 )
        {
            EbRaiseExceptionCode(VT_UNKNOWN);
        }
        if ( !bCurrency )
        {
            if ( pVar->cyVal.Hi && (*(*pVar->cyVal.Hi + 28))(pVar->cyVal.Hi, &pVar) >= 0 )
            {
                return pVar;
            }
            goto LABEL_50;
        }
        if ( pVar->vt & VT_BYREF && (v11 = pVar->lVal, *v11) && *(*v11 + 2) & 0x20 )
        {
            v12 = *pVar->plVal;
        }
        else if ( pVar->vt & VT_BYREF || (v12 = pVar->lVal, !(*(v12 + 2) & 0x20)) || !*(v12 - 4) )
        {
            v3 = off_6524FDD0;
            goto LABEL_17;
        }
        v13 = (*(**(v12 - 4) + 28))(*(v12 - 4), &bstrString);
        if ( v13 < 0 )
        {
            v14 = EberrOfHresult(v13);
            EbRaiseExceptionCode(v14);
        }
        v3 = bstrString;
LABEL_17:
        if ( bCurrency )
        {
            v5 = wcslen(v3);
            pVar = SysAllocStringLen(0, v5 + 2);
            if ( !pVar )
            {
                EbRaiseExceptionCode(14);
            }
            sub_65012860(&pVar->vt, v5 + 3, v3);
            v6 = 2 * v5;
            *(&pVar->vt + v6) = 40;
            *(&pVar->wReserved1 + v6) = 41;
            *(&pVar->vt + v5 + 2) = 0;
            goto LABEL_13;
        }
LABEL_12:
        pVar = SysAllocString(v3);
        if ( !pVar )
        {
            EbRaiseExceptionCode(14);
        }
LABEL_13:
        SysFreeString(bstrString);
        return pVar;
    }
    if ( vt == VT_UNKNOWN )
    {
        goto LABEL_6;
    }
    if ( vt < 0 )
    {
LABEL_44:
        EbRaiseExceptionCode(458);
    }
    if ( vt <= 8 )
    {
DecimalOrUI1:
        v3 = (&off_6524FD88)[2 * vt];
        goto LABEL_17;
    }
    if ( vt != 9 )
    {
        if ( vt <= 9 )
        {
            goto LABEL_44;
        }
        if ( vt > 11 )
        {
            if ( vt == 12 )
            {
LABEL_19:
                if ( dword_65246024 )
                {
                    EbRaiseExceptionCode(VT_UNKNOWN);
                }
                goto DecimalOrUI1;
            }
            goto LABEL_44;
        }
        goto DecimalOrUI1;
    }
LABEL_6:
    if ( bCurrency )
    {
        goto LABEL_19;
    }
    if ( dword_65246024 )
    {
        EbRaiseExceptionCode(VT_UNKNOWN);
    }
    if ( pVar->vt & 0x4000 )
    {
        v2 = *pVar->plVal;
    }
    else
    {
        v2 = pVar->lVal;
    }
    if ( !v2 )
    {
        v3 = L"Nothing";
        goto LABEL_12;
    }
    if ( (**v2)(v2, &IID_IProvideClassInfo, &v16) < 0 )
    {
        if ( (**v2)(v2, &IID_IDispatch, &v15) < 0 )
        {
LABEL_41:
            v3 = (&off_6524FD88)[2 * vt];
            goto LABEL_12;
        }
        v7 = (*(*v15 + 16))(v15, 0, 1033, &v18);
        v8 = v15;
    }
    else
    {
        v7 = (*(*v16 + 12))(v16, &v18);
        v8 = v16;
    }
    (*(*v8 + 8))(v8);
    if ( v7 < 0 )
    {
        goto LABEL_41;
    }
    v9 = (*(*v18 + 48))(v18, -1, &pVar, 0, 0, 0);
    (*(*v18 + 8))(v18);
    if ( v9 < 0 )
    {
LABEL_50:
        v3 = pVar;
        goto LABEL_12;
    }
    result = pVar;
    if ( 95 != pVar->vt )
    {
        return result;
    }
    v10 = SysAllocStringLen(&pVar->wReserved1, (pVar[-1].cyVal.Hi >> 1) - 1);
    SysFreeString(&pVar->vt);
    result = v10;
    return result;
}

Điều quan trọng là bác @ThangCuAnh đã giúp lấy được tên hàm API của hàm TypeName cùng prototype, tôi viết lại code để chúng ta dùng trong Delphi như sau.

1. Các bạn thao khảo cách dùng bên VB/VBA (mang tính học tập vì trong VBA hàm TypeName đã có.

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

2. Cách khai báo API hàm rtcTypeName() để dùng trong Delphi. Nếu chúng ta tạo Add-in thì đây là tư liệu quý.

Code dưới đây tôi viết trong chương trình chính dạng Console (màn hình đen trắng). Các bạn làm trình tự như sau.

1. Từ Delphi, tạo application dạng Console (nếu ai có add-in rồi thì chỉ cần code chính của nó).
2. Paste đoạn code dưới đây.
3. Chạy Excel với quyền "Run as administrator"
4. Chạy progam trong Delphi (F9)

Mã:
program Test_VBA_API.dproj;
//This code call function API in VBAxxx "rtcTypeName()". In VBA its name is TypeName()
{$APPTYPE CONSOLE}

{$R *.res}

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

type
  TFuncVBAPI_rtcTypeName = function(var v: OleVariant): PWideChar; stdcall;

var sName: WideString;
  h: HMODULE;
  pFunc: TFuncVBAPI_rtcTypeName;
  App: OleVariant;
  v: 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');
      v := App.ActiveCell;
      sName := PWideChar(TFuncVBAPI_rtcTypeName(pFunc)(v));
      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
thôi tạm dừng đó Anh ... lúc nào Em coi lại các bước sau
Em vẫn làm theo hướng dẫn bài 811 mà ... Chạy Excel Run As
View attachment 222214
Tóm lại đã hết "EOleSysError: CoInitialize has not been called". Chỉ còn "EOleSysError: Operation unavailable"? Nếu bạn đã khởi động Excel mà còn lỗi này thì có nghĩa là lỗi không còn ở code nữa. Tôi không rõ lắm về các quyền vì tôi chỉ quan tâm tới XP Home của tôi thôi. Nhưng nếu nói về quyền thì theo lôgíc của tôi phải phân quyền cho Project1.exe. Tức run project1.exe với quyền "Run as administrator". Dù sao thì theo tôi lỗi không nằm trong code nữa.
 
Upvote 0
Tóm lại đã hết "EOleSysError: CoInitialize has not been called". Chỉ còn "EOleSysError: Operation unavailable"? Nếu bạn đã khởi động Excel mà còn lỗi này thì có nghĩa là lỗi không còn ở code nữa. Tôi không rõ lắm về các quyền vì tôi chỉ quan tâm tới XP Home của tôi thôi. Nhưng nếu nói về quyền thì theo lôgíc của tôi phải phân quyền cho Project1.exe. Tức run project1.exe với quyền "Run as administrator". Dù sao thì theo tôi lỗi không nằm trong code nữa.

AH. ĐÚng rồi. Bạn Mạnh thử chạy Delphi với quyền "Run as administrator" xem.
 
Upvote 0
Tóm lại đã hết "EOleSysError: CoInitialize has not been called". Chỉ còn "EOleSysError: Operation unavailable"? Nếu bạn đã khởi động Excel mà còn lỗi này thì có nghĩa là lỗi không còn ở code nữa. Tôi không rõ lắm về các quyền vì tôi chỉ quan tâm tới XP Home của tôi thôi. Nhưng nếu nói về quyền thì theo lôgíc của tôi phải phân quyền cho Project1.exe. Tức run project1.exe với quyền "Run as administrator". Dù sao thì theo tôi lỗi không nằm trong code nữa.
Chạy tới lui các kiểu nó cũng báo cái dòng màu đỏ đó Anh còn lại ko thấy chi hết ... còn tại sao Em Tịt ....!
Em thử chạy cái File Delphi *.exe Run As nó cũng thế
 
Lần chỉnh sửa cuối:
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom