Probleme bei Multi language *gelöst* - aber noch offene Frage



  • Hallo zusammen,

    ich habe ein Programm zur Pflege bei dem es jetzt Probleme mit der Sprachanzeige gibt, das allerdings nur bei einer Sprache...Chinesisch.

    Das Programm ermittelt beim Start die eingestellte Sprache und läd die zugehörige Sprachdll. Die Resourcestrings überdecken nun die Strings der dll in der der eigentl. Code läuft.

    Mache ich jetzt z.b.

    CString csMessage;
    csMessage.LoadString(<MeineStringID>)
    //String anzeigen
    

    funktioniert das für (fast) alle Sprache. Deutsch,Englisch,Italienisch,Spanisch alles kein Problem. Auch Chnesisch funktioniert anfangs.
    Das Problem kommt erst, wenn ich die Strings in einem neuen Thread außerhalb des GUI Threads laden will. Dann bekomme ich nur "?" geliefert. (Bei allen anderen Sprachen außer Chinesisch funktioniert es)

    Die Strings sind in der Resourcedll korrekt übersetzt, das habe ich bereits überprüft. Auch die System locale habe ich auf Chinesisch gesetzt. (Zeichen können theoretisch angezeigt werden, sieht man ja an der Ausgabe im GUI Thread)

    Das OS ist Windows 8.1 aber auch unter Windows 7 ist das Problem zu beobachten.

    Hat evtl. Irgendjemand von euch eine Idee dazu?

    Gruß
    Sascha


  • Mod

    We hast Du den neuen Thread gestartet?

    Du solltest AfxBeginThread verwenden, damit die MFC Zeiger gerade auch bzgl. der Ressource DLLs normal funktionieren.



  • Hi,

    We hast Du den neuen Thread gestartet?

    Du solltest AfxBeginThread verwenden, damit die MFC Zeiger gerade auch bzgl. der Ressource DLLs normal funktionieren.

    ja wurde so gemacht.

    m_pThread = AfxBeginThread(<WorkerMethodName>,NULL,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
    // ...
    m_pThread->ResumeThread();
    

  • Mod

    Dann sollte es eigentlich gehen.

    Debugge doch mal in den LoadString hinein. Du hast doch den kompletten Sourcecode.

    Wird wirklich die Unicode Version aufgerufen?



  • Was ich vorhin vergessen habe zu erwähnen, in Debug bekomme ich gar keine Übersetzung, da bekomme ich die deutschen Originaltexte... 😡

    Ich habe mich mal vor gedebugged und komme an folgende Codestelle in der dllinit.cpp welche unter Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc liegt. (Ich habe die Methode mal komplett kopiert- ich hoffe es ist nicht zu unübersichtlich jetzt 🙄 )

    HINSTANCE AFXAPI AfxFindStringResourceHandle(UINT nID)
    {
    	HINSTANCE hInst;
    	CSimpleArray<HINSTANCE> hInstCache;
    
    	// first check the main module state
    	AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
    	if (!pModuleState->m_bSystem)
    	{
    		hInst = AfxGetResourceHandle();
    		if (AtlGetStringResourceImage(hInst, nID) != NULL)
    		{
    			// found a non-zero string in app
    			return hInst;
    		}
    	}
    
    	// check non-system DLLs in proper order
    	AfxLockGlobals(CRIT_DYNLINKLIST);
    	CDynLinkLibrary* pDLL;
    	for (pDLL = pModuleState->m_libraryList; pDLL != NULL; pDLL = pDLL->m_pNextDLL)
    	{
    		if (!pDLL->m_bSystem && (hInst = pDLL->m_hResource) != NULL)
    		{
    			// cache the hInst for subsequent FindResource call
    			hInstCache.Add(hInst);
    		}
    	}
    	AfxUnlockGlobals(CRIT_DYNLINKLIST);
    
    	// Search the hInst for the resource outside CRIT_DYNLINKLIST lock because the search requires
    	// a loader lock, which can potentially cause a deadlock when we are loading/unloading modules.
    	if (hInstCache.GetSize() > 0)
    	{
    		for (int iIndex = 0; iIndex < hInstCache.GetSize();iIndex++)
    		{
    			hInst = hInstCache[iIndex];
    			if (AtlGetStringResourceImage(hInst, nID) != NULL)
    			{
    				AfxLockGlobals(CRIT_DYNLINKLIST);
    				for (pDLL = pModuleState->m_libraryList; pDLL != NULL; pDLL = pDLL->m_pNextDLL)
    				{
    					if (!pDLL->m_bSystem && pDLL->m_hResource == hInst)
    					{
    						hInstCache.RemoveAll();
    						AfxUnlockGlobals(CRIT_DYNLINKLIST);
    						return hInst;
    					}
    				}
    				AfxUnlockGlobals(CRIT_DYNLINKLIST);
    			}
    		}
    		hInstCache.RemoveAll();
    	}
    
    	// check language specific DLL next
    	hInst = pModuleState->m_appLangDLL;
    	if (hInst != NULL && (AtlGetStringResourceImage(hInst, nID) != NULL))
    	{
    		// found a non-zero string in language DLL
    		return hInst;
    	}
    
    	// check the system module state
    	if (pModuleState->m_bSystem)
    	{
    		hInst = AfxGetResourceHandle();
    		if (AtlGetStringResourceImage(hInst, nID) != NULL)
    		{
    			// found a non-zero string in app
    			return hInst;
    		}
    	}
    
    	// check system DLLs in proper order
    	AfxLockGlobals(CRIT_DYNLINKLIST);
    	for (pDLL = pModuleState->m_libraryList; pDLL != NULL; pDLL = pDLL->m_pNextDLL)
    	{
    		if (pDLL->m_bSystem && (hInst = pDLL->m_hResource) != NULL)
    		{
    			// cache the hInst for subsequent FindResource call
    			hInstCache.Add(hInst);
    		}
    	}
    	AfxUnlockGlobals(CRIT_DYNLINKLIST);
    
    	// Search the hInst for the resource outside CRIT_DYNLINKLIST lock because the search requires
    	// a loader lock, which can potentially cause a deadlock when we are loading/unloading modules.
    	if (hInstCache.GetSize() > 0)
    	{
    		for (int iIndex = 0; iIndex < hInstCache.GetSize();iIndex++)
    		{
    			hInst = hInstCache[iIndex];
    			if (AtlGetStringResourceImage(hInst, nID) != NULL)
    			{
    				AfxLockGlobals(CRIT_DYNLINKLIST);
    				for (pDLL = pModuleState->m_libraryList; pDLL != NULL; pDLL = pDLL->m_pNextDLL)
    				{
    					if (pDLL->m_bSystem && pDLL->m_hResource == hInst)
    					{
    						hInstCache.RemoveAll();
    						AfxUnlockGlobals(CRIT_DYNLINKLIST);
    						return hInst;
    					}
    				}
    				AfxUnlockGlobals(CRIT_DYNLINKLIST);
    			}
    		}
    		hInstCache.RemoveAll();
    	}
    
    	// did not find it
    	return NULL;
    }
    

    Was ich in Debug gesehen habe:
    In Zeile 3 bis 28 werden geladene Module in ein CSimpleArray gesteckt. Ich habe die handvoll Adressen die da ermittelt werden
    mit der Modulliste abgeglich und meine Resource Dll ist nicht dabei.
    Die String ID wird jedoch in der "Main" Dll gefunden und somit diese HINSTANCE bei Zeile 47 returnt.

    Die Mechanik ist bei dem programm was ich hier habe folgendermaßen aufgezogen.

    1. Nutzdll mit dem eigentlichen Nutzcode und den deutschen Strings wird erstellt.
    2. Generierung einer Resource DLL die nur deutsche Strings enthällt auf basis der rc datei von 1)
    3. Generierung anderer Sprach DLLs aus der deutschen resource Dll wo die texte bei den einzelnen StringIDs entsprechend übersetzt sind.

    Ich habe nicht viel Ahnung wie man sowas mit Übersetzung und ausgelagerten Resourcen macht, aber diese MEchanik als Grundlage genommen verstehe ich den oberen Code nicht. Demnach würde erst in den resource Dlls nach dem String geschaut werden,
    wenn die ID nicht in der Dll mit dem Nutzcode gefunden wurde. Und warum funktioniert es dann unter Release aber nicht in Debug? Zufall? 😕
    (Ich hoffe ich hab es nicht zu umstädnlich ausgedrückt)

    Das ist erstmal der Grund warum bei mir im Debug gar keine Übersetzung funktioniert aber der Grund für das eigentliche Problem ist es nicht...denke ich confused: 😕 😕

    Grüße
    Sascha

    PS:
    So das war viel Text, ein Danke schonmal an die, die es sich durchlesen 🤡



  • Hi, ich hab es soweit gelöst...

    Ich hatte zuerst das Problem, dass die Übersetzung in Debug nicht funktioniert hat.
    Es hat ein AfxSetResourceHandle gefehlt. Warum es aber im Release bisher ohne dem geht ist mir Schleierhaft.

    Da ich debuggen konnte habe ich gesehen, dass der String richtig aus der Dll geholt wird.

    In der LoadString methode vom CString wird dann ein StringTraits::ConvertToBaseType was dann selbst einen ::WideCharToMultiByte macht und genau hier war der Knackpunkt...
    Im watch habe ich gesehen, dass der source string richtig ist, das Ergebnis der Konvertierung dann allerdings falsch.

    Als Codepage steckt MFC CP_THREAD_ACP in den ::WideCharToMultiByte...

    💡 💡 Also stimmt die Codepage nicht die da im Thread verwendet wird. 💡 💡

    Mache ich jetzt im Thread SetThreadLocale(LOCALE_SYSTEM_DEFAULT) ...tada chinesischer String :p

    Jetzt habe ich aber noch Folgende Frage, warum wird die system locale, die ja anscheinend im MainThread verwendet wird nicht in dem anderen Thread verwendet.?


  • Mod

    Weil jeder Thread seine eigene Locale hat. Es heißt eben SetThreadLocale


Log in to reply