질문
나는 Microsoft Word에 애드온을 만들었습니다. 사용자가 버튼을 클릭하면 Microsoft Word 문서 목록을 Filtered HTML로 내보내는 기능으로 여러 프로세스를 실행합니다. 이 애드 온은 잘 동작합니다만,
많은 양의 파일을 처리 할 때 코드가 떨어지네요.
파일 변환이 완료되고 다음 함수를 호출하면 앱이 오동작하면서 Visual Studio가 다음과 같은 메시지들 뱉어 냅니다.
Managed Debugging Assistant 'DisconnectedContext' has detected a problem in 'C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE'. Additional information: Transition into COM context 0x56255b88 for this RuntimeCallableWrapper failed with the following error: System call failed. (Exception from HRESULT: 0x80010100 (RPC_E_SYS_CALL_FAILED)). This is typically because the COM context 0x56255b88 where this RuntimeCallableWrapper was created has been disconnected or it is busy doing something else. Releasing the interfaces from the current COM context (COM context 0x56255cb0). This may cause corruption or data loss. To avoid this problem, please ensure that all COM contexts/apartments/threads stay alive and are available for context transition, until the application is completely done with the RuntimeCallableWrappers that represents COM components that live inside them.
몇가지 테스트를 해보니, 파일 변환 부부만 제외하고 단순히 코드를 다 지워보고 난 후에 문제가 없어 진다는 것을 획인 했습니다. 나는 이 문제를 또 하나의 버튼을 만들고 그 동작에다 그 나머지 코드 부분을 할당 해서 문제를 해결했습니다.
여기서 문제는 사용자에게 두 개의 버튼을 제공하고 싶지 않다는 것이죠. 다양한 다른 기사들을 쭉 읽어 보니 내 코드에 메모리 또는 스레딩 문제가 있는 게 아닌가 하고 생각이 됩니다.
내가 읽었던 답변들은 그래서 다음으로 무엇을 해봐야 하는지에 대해선 전혀 도움이 안됐습니다.
내가 하고 싶은 것은.
1- 변환을 실행하고
2- 변환에서 생긴 스레드/메모리 정리 문제를 해결하고,
3- 코드를 계속 사용 하고 싶다.
입니다.
불행히도, 2번 방법을 어떻게 해야 할 지도 모르겠고 정말로 그게 가능 할 것인지도 모르겠습니다.
도움을 부탁드립니다.
답변:
당신이 받은 관리 디버깅 도우미(managed debugging assistant) 진단은 정말 무슨 소린지 모르겠다고 하겠지만, 실제 일어난 문제를 정확하게 설명해 주는 메시지의 일부입니다. 당신의 프로그램은 스레딩과 관련해서 세 번째로 흔한 문제인 Firehose 문제가 발생 했습니다.
이 재난(?)은 당신의 코드 문제가 아니라 Word 공정 내부에서 오류이기 때문에 진단하기 더 어렵습니다.
이 알아 들을 수 없는 오류 메시지 보다는 알아들을 수 있게 말하자면, 당신이 만든 Office 프로그램으로 들어간 interop 호출들이 대기열에 쌓였고(큐잉), 자신들의 실행 순서를 기다리다 문제가 생겼다는 것입니다.
오류 코드가 암시하는 것은 "system call(시스템 호출)"의 하부에 있는 PostMessage()를 말하는 것입니다.
이러한 대기열(큐)을 만들 때마다 생기는 위험은 대기열이 너무 커질 수 있다는 것입니다.
생산자(당신의 프로그램)가 소비자(Office 프로그램)가 항목을 소비하는 것보다 훨씬 더 빨리 대기열에 항목을 추가가 될 때 이러한 위험이 발생하는 데 이것이 바로 firehose problem 입니다.
생산자의 (추가)속도가 느려지지 않으면, 대기열은 제한 없이 확장될 것이고,
만약 이 확장에 제한을 두지 않는다면 최소한 프로세스에서 메모리가 부족하여 무언가는 실패하게 될 것입니다.
우리가 이 문제를 직접적으로 접근하는 것은 허용되지 않습니다. PostMessage()가 사용하는 기본 대기열(큐)은 OS에 의해 보호됩니다.
대기열이 이미 10,000개의 메시지가 쌓이면 Windows는 호출에 실패합니다.
이는 RPC가 복구 방법을 모르거나 복구를 시도해서는 안 되는 치명적인 오류(fatal error) 입니다. 뭔가 어색해 졌고 못생겨졌다는 것이죠. OS는 당신의 프로그램에 다음의 오류 코드를 반환하여 이를 알려줍니다.
이 오류코드가 바로 RPC_E_SYS_CALL_FAILED 입니다.
별로 좋지 않은 일이 당신의 프로그램에서 일어났으며, CLR도 이 에러 코드에 대한 복구 방법을 알지 못하며 마찬가지로 당신코드도 마찬가지라는 것입니다. 그래서 끝나버렸고, 당신이 만든 interop 호출이 손실되어 Word가 이를 실행하지 못했다는 것입니다.
이 이상야릇한 문제에 대해 완전히 신뢰할 수 있는 해결 방법을 찾는 것은 그리 간단하지 않습니다.
이 문제는 모든 interop 호출에서 발생할 수 있으므로 예외를 잡아서 다시 실행하는 것은 매우 비실용적인 일입니다. 그러나 Q+D 수정은 매우 간단하다는 것만 알아 둡시다. 일반적인 문제는 당신의 프로그램이 너무 빨리 실행되기 때문에 조잡해 보이지만 Thread.Sleep() 또는 Task.Delay() 호출로도 항상 문제를 해결할 수 있다는 것입니다.
그냥, 충분히 지연시킨다고 가정한다면 말입니다.
내 생각에는 아무도 해당 재현할 수 있는 코드를 보여주지 않아서 당신의 문제가 콘솔 모드 앱이나 프로그램에서 작업자 스레드를 사용했는 지는 모르겠습니다. 만약 앱의 형태가 콘솔 모드인 경우 [STAThread] 속성을 Main() 메서드에 적용해 보십시오.
작업자 스레드(worker thread)라면 스레드를 시작하기 전에 Thread.SetApartmentState()를 호출할 수 있지만 해당 작업자 스레드 상에서 Application 인터페이스까지 생성 될 수 있다는 것을 매우 중요하게 고려 해야 합니다. 그렇지 않으면 애드 온(추가 기능)에 대한 해결 방법이 안 될 수 있습니다.
이런 해결 방법이 효율적이지 않거나 너무 실용적이지 않을 경우라면 당신의 프로그램을 자동적으로 느리게 만들어서 가끔씩 Office 프로그램에서 무언가 다시 읽어 냄으로써 대기열(큐)가 비었는지 확인 하는 것입니다.
바보같이 들리겠지만, 어떤 프로퍼티 getter 호출로 이렇게 할 수 있을 것입니다. Office 프로그램이 (이 속도를) 따라 잡을 때까지는 프로퍼티 값을 얻을 수 없을 거니까 말이죠.
여전히 실패할 수 있겠지만 interop 호출은 60초 제한 시간도 있습니다. 하지만 프로그램에서 CoRegisterMessageFilter()를 호출하여 시간 초과가 발생할 때 실행되는 콜백을 만드는 것도 당신이 이 문제를 해결 할 수 있는 방법이 됩니다.
역시나 엉뚱한 얘기로 들리겠지만, 쉽게 다음 코드를 그대로 사용할 수 있을 것입니다.
- 그 다음 코드
이상.
'프로그래밍' 카테고리의 다른 글
아파치 POI로 하는 Excel 프로그래밍 (0) | 2022.09.03 |
---|---|
Guice - Google (2) | 2022.08.30 |
[자바]특정 이미지에 투명도 입히기 (0) | 2022.04.11 |
[자바] Argument Parser (0) | 2022.04.10 |
자바에서 enum 사용 (0) | 2022.04.10 |