Tôi không hoàn toàn chắc chắn liệu mình đã giải quyết được vấn đề này hay chưa nhưng đây là những gì tôi thấy và những gì tôi认为Chuyện gì đang xảy ra vậy?
Tôi có chương trình Win32 được viết chủ yếu bằng C để tải C++ DLL. DLL chuyển dữ liệu từ chương trình C sang ứng dụng khác thông qua đối tượng COM—một đối tượng có thể được chính DLL khởi tạo. Tất cả điều này rõ ràng hoạt động tốt trong ít nhất Windows XP và Windows 7 (có thể là Win95 và Win98, tôi cần tìm hiểu sâu hơn về lịch sử mã để tìm hiểu thời điểm giao diện này được giới thiệu), nhưng trong Windows 10, chương trình gặp sự cố tại FreeLibrary () được gọi trong DLL này.
Khi kiểm tra trong trình gỡ lỗi, DLL_DETACH_PROCESS dường như được xử lý thành công (không có mã nào được thực thi trong khi xử lý thông báo). Sự cố xảy ra khi (hoặc cùng lúc) rời khỏi mã từ điểm vào.
Nếu tôi tiếp tục Bước vào, cuối cùng tôi sẽ thấy một tệp tiêu đề có tên utilcls.h, có vẻ như là một trong các tệp tiêu đề Borland C Builder 6. Tôi tin rằng mã mẫu có liên quan đến việc các đối tượng COM bị phá bỏ. Lệnh gọi Unbind() đã thành công và đây là dòng mã cuối cùng tôi có thể xử lý trước khi gặp sự cố.
Nếu tôi sử dụng cửa sổ CPU của trình gỡ lỗi và tiếp tục bước, tất cả những gì còn lại dường như có liên quan đến việc giải phóng bộ nhớ trước khi xảy ra sự cố, nhưng CPU phải thực hiện rất nhiều bước để đạt được điều đó.
Sự cố đã gây ra ngoại lệ APPCRASH 0xc0000602, trả về Combase.dll.
Miễn là FreeLibrary không được gọi cho DLL đó thì ứng dụng sẽ đóng thành công, nhưng tôi giả định rằng lệnh gọi FreeLibrary rất quan trọng.
Đối tượng COM được ứng dụng chia sẻ dữ liệu giải phóng trước lệnh gọi FreeLibrary(), cho phép ứng dụng tắt. Giả thuyết hiện tại của tôi là một số thao tác hủy liên kết xảy ra khác nhau trong các hệ điều hành mới hơn, điều này gây ra sự cố nhưng tôi không biết làm cách nào để biết chắc chắn.
Câu hỏi của tôi:
Nếu người khác biết rõ hơn họ đang làm gì thì điều gì có thể gây ra vụ tai nạn?
Bước tiếp theo trong việc cố gắng gỡ lỗi nó là gì? Tôi đã cạn kiệt kiến thức về môi trường gỡ lỗi mà tôi đang sử dụng và không biết đủ về COM hoặc DLL để biết phải hỏi gì tiếp theo.
Một số đầu ra của trình gỡ lỗi được RbMm yêu cầu:
0:000:x86>t
ntdll_77b40000!RtlIsCriticalSectionLockedByThread+0x1b:
77b7256b c20400 ret 4
0:000:x86>t
combase!DecrementMTAUsageHelper+0x5b:
7527a2d6 85c0 kiểm tra eax,eax
0:000:x86> r eax
eax=00000001
0:000:x86>t
combase!DecrementMTAUsageHelper+0x5d:
7527a2d8 0f859a000000 jne combase!DecrementMTAUsageHelper+0xfd (7527a378) [br=1]
0:000:x86>t
combase!DecrementMTAUsageHelper+0xfd:
7527a378 e89e9e0f00 gọi tổ hợp!CrashProcessWithWERReport (7537421b)
Tại thời điểm này, ngăn xếp trông gần như thế này:
ChildEBP RetAddr Args cho con
0019f9b8 7527a37c 063f4248 753d8448 00000000 combase!CrashProcessWithWERReport+0x35
0019f9e8 75292bfc 753d8448 7529257e 00000000 combase!DecrementMTAUsageHelper+0x101
(Nội tuyến) -------- -------- -------- -------- combase!DecrementMTAUsage+0x9
0019f9f0 7529257e 00000000 00000000 00000000 combase!CDllHost::MTAUninitializeApartmentOnly+0xe
0019fa08 7527543a 00000000 063f4248 00712410 combase!CDllHost::ClientCleanupFinish+0x4d
0019fa30 75276361 00000000 0019fa8c 00000000 combase!DllHostProcessUninitialize+0xa0
0019fa58 7527a452 000d06f6 00712410 00000000 combase!ApartmentUninitialize+0xe4
0019fa70 752c2a1e 000d06f6 00712e18 00712e80 combase!wCoUninitialize+0xd0
0019fa94 74ed3e58 00000003 74c17ff1 a6d0e607 tổ hợp!CoUninitialize+0x7e
0019fa9c 74c17ff1 a6d0e607 000b0792 74ed48f0 imm32!CtfImmCoUninitialize+0x48
0019fb7c 74809ea6 00050004 000d06f6 00000000 msctf!TF_Notify+0x581
0019fb98 748080dc 00050004 000d06f6 00000000 user32!CtfHookProcWorker+0x36
0019fbe0 74807fa6 0019fc34 0019fc24 00000000 user32!CallHookWithSEH+0x5c
0019fc08 77bb0006 0019fc24 00000018 0019fc80 user32!__fnHkINDWORD+0x26
0019fc38 710623fb 000b0792 04ff11aa 05480e70 ntdll!KiUserCallbackDispatcher+0x36
0019fc50 050364e4 000b0792 050376d8 05480e70 trợ giúp ứng dụng!DWM8AND16BitHook_DestroyWindow+0x2b
0019fc8c 05051007 00000000 05055034 00000001 myDLL!myCOMObject_tlbFinalize+0x408a4
0019fcb4 050511c6 0019fcd0 00000001 04ff1318 myDLL!myCOMObject_tlbFinalize+0x5b3c7
0019fcd8 04ff13d3 05055034 77badcce 04ff0000 myDLL!myCOMObject_tlbFinalize+0x5b586
0019fd00 77b807c6 04ff1318 04ff0000 00000000 myDLL+0x13d3
0019fd50 77b6aa5e 00000000 00000000 259704e5 ntdll!LdrpCallInitRoutine+0x43
0019fdb8 77b6e6c8 00000000 0071dd60 00000000 ntdll!LdrpProcessDetachNode+0xbb
0019fdd8 77b6e5af 25970745 0071e560 c000022d ntdll!LdrpUnloadNode+0x100
0019fe18 77b6e4f6 004afcc4 004ae3a4 04ff0000 ntdll!LdrpDecrementModuleLoadCountEx+0xa7
0019fe38 746e9d56 04ff0000 006e33c5 00000000 ntdll!LdrUnloadDll+0x86
0019fe4c 0049261c 04ff0000 00000000 00493034 KernELBASE!FreeLibrary+0x16
0019fe64 00441895 004afc98 fffffffe 0019fee8 rpopdbg!_GetExceptDLLinfo+0x914bf
Hiện đang xử lý phần còn lại, nhưng tôi đoán tôi cần tìm ra cách vệ sinh các đối tượng COM đúng cách? Có lẽ để đáp lại DLL_DETACH_PROCESS?
Sự cố làm tăng APPCRASH ngoại trừ 0xc0000602, quay lại Combase.dll
com.dll
sử dụng 0xc0000602
(STATUS_FAIL_FAST_EXCEPTION
) chỉ mã từ
void CrashProcessWithWERReport();
(Sử dụng mã này gọi TăngThất bạiNhanhNgoại lệ
)
CrashProcessWithWERReport
Chỉ với 2 điều kiện từ Giảm dầnMTAUsageHelper
gọi - MãGiảmMTAUsage
Được gọi nhiều hơn CoIncrementMTAUsage
hoặc(Tôi gần như chắc chắn là vì lý do này)Giảm dầnMTAUsageHelper
Phần quan trọng của Trình tải được gọi khi luồng gọi được gọi - do đó trong quá trình tải hoặc dỡ tải DLL. từ MSDN
Không gọi CoDecrementMTAUsage trong quá trình tắt máy hoặc bên trong dllmainBạn có thể gọi CoDecrementMTAUsage trước cuộc gọi để bắt đầu quá trình tắt máy.
Vì vậy tôi đoán - một số cuộc gọi mã MãGiảmMTAUsage
Trong quá trình dỡ bỏ DLL của bạn (khi bạn gọi Thư viện miễn phí
giờ)
DLL của bạn không thể được gọi trực tiếpCoIncrementMTAUsage
/MãGiảmMTAUsage
Bởi vì API mới này tồn tại từ win 8 (cũng kiểm tra mã của bạn trên win 8.1 - tôi nghĩ nó cũng sẽ bị lỗi), nhưng api này có thể được gọi gián tiếp từ các thành phần hệ thống khác.
Tôi có thể cho rằng DLL của bạn không giải phóng trực tiếp một số tài nguyên đã sử dụng hoặc bạn gọi khi DLL vẫn giữ một số tài nguyên Thư viện miễn phí
(Vậy bạn gọi Thư viện miễn phí
không có lệnh dọn dẹp thích hợp nào được thực hiện tới DLL) và do đó tài nguyên bắt đầu được giải phóng trong quá trình dỡ tải (MãGiảmMTAUsage
)
các bước tiếp theo trong việc cố gắng gỡ lỗi này là gì?
Bạn cần sử dụng các tệp ký hiệu để gỡ lỗi (ví dụ: sử dụng winDbg). hiện hữu Giảm dầnMTAUsageHelper
,MãGiảmMTAUsage
Đặt điểm dừng tại và có thể CoIncrementMTAUsage
- Tôi gọiRtlIsCriticalSectionLockedByThread
Nó có được trả lại chính xác không? TRUE
(API này bắt đầu từ Giảm dầnMTAUsageHelper
bắt đầu gọi).
Trong mọi trường hợp, trong Giảm dầnMTAUsageHelper
Ngăn xếp cuộc gọi luồng được xuất bản tại điểm gọi (ngay trước khi xảy ra sự cố), cũng có thể tại CoIncrementMTAUsage
Xuất bản trên
-------------------- biên tập-------------------- ----
Hiển thị bằng cách xem dấu vết ngăn xếp, lệnh gọi DLL của bạn từ DllMain Phá hủy cửa sổ
.
apphelp!DWM8AND16BitHook_DestroyWindow
Điều này sai vì hai lý do - Đầu tiên - Đọc bài viết này -
Chuỗi nhận được thông báo DLL_PROCESS_DETACH không nhất thiết phải là chuỗi nhận được thông báo DLL_PROCESS_ATTACH. Bạn không thể làm bất kỳ điều gì với mối quan hệ của chuỗi trong trình xử lý DLL_PROCESS_ATTACH hoặc DLL_PROCESS_DETACH vì bạn không có gì đảm bảo về chuỗi nào sẽ được gọi để xử lý các thông báo quy trình này. Ví dụ kinh điển về điều này mà tôi được biết nhóm Hỗ trợ nhà phát triển gặp phải với tần suất đáng báo động là một DLL tạo ra các tệp . một cửa sổ trong trình xử lý DLL_PROCESS_ATTACH của nó và hủy nó trong trình xử lý DLL_PROCESS_DETACH của nó.
Nhưng lỗi của bạn là do nguyên nhân khác chưa được liệt kê trong bài - DllMain có rất nhiều hạn chế , không có gì có thể được gọi bên trong. mặc dù Phá hủy cửa sổ
Không được liệt kê trực tiếp ở đây, nhưng như trường hợp của bạn cho thấy - đây là cuộc gọi bất hợp pháp (mặc dù chúng tôi đang gọi nó trên cùng một chuỗi đã tạo cửa sổ này) - khi cửa sổ của bạn bị phá hủy imm32 .CtfImmNotify(msctf!TF_Notify)
gọi điện
0019fa9c 74c17ff1 a6d0e607 000b0792 74ed48f0 imm32!CtfImmCoUninitialize+0x48
0019fb7c 74809ea6 00050004 000d06f6 00000000 msctf!TF_Notify+0x581
0019fb98 748080dc 00050004 000d06f6 00000000 user32!CtfHookProcWorker+0x36
0019fbe0 74807fa6 0019fc34 0019fc24 00000000 user32!CallHookWithSEH+0x5c
结果Đồng khởi tạo Được gọi từ DllMain !
từ MSDN
không gọi CoInitialize, CoInitializeEx hoặc CoUninitialize từ hàm DllMain.
nó đây rồiCUỐI CÙNG Đồng khởi tạo称为 GiảmMTAUsage
, được thực hiện bằng cách gọi RtlIsCriticalSectionLockedByThread
và được gọi CrashProcessWithWERReport
Hãy chắc chắn rằng chúng tôi đang ở bên trong khóa nạp.
Giải pháp?
Tất nhiên điều tốt nhất là sửa DLL, nhưng nếu điều này không thể thực hiện được - hãy xem xét cách "hack" tiếp theo
HRESULT hr = CoInitialize(0); // giả sử chúng ta đang ở STA
Thư viện miễn phí (hDLL);
if (0 <= hr) CoUninitialize();
Với cái nàyĐồng khởi tạoTất nhiên, dù thế nào đi chăng nữa, nó sẽ bắt đầu từ imm32!CtfImmCoUninitialize
gọi, nhưng đây sẽ là KHÔNG CUỐI CÙNG không được khởi tạo, vì vậy GiảmMTAUsage
sẽ không được gọi
Tôi là một lập trình viên xuất sắc, rất giỏi!