none
OFFICEアプリへのOLE貼り付け RRS feed

  • 質問

  • MFCアプリケーションからクリップボード経由で、OFFICE2010、2013(EXCEL,WORD,POWERPOINT,VISIO)へOLE貼り付けを行っています。UNICODE文字対応をするため拡張メタファイルも対応しました。

    OFFICEの中で、POWERPOINTだけが、OLE貼り付け時のイメージを、「CF_METAFILEPICT」形式を要求するので、結果、メタファイル(WMF)となってしまい、韓国語、中国語などで書かれた文字は、文字化けしたものが表示されてしまいます。

    EXCEL,WORD,VISIOは、拡張メタファイルを要求してくるので、文字化けが起こりません。

    しかしながらEXCELに韓国語、中国語を入力して、POWERPOINTにOLE貼り付けしても文字化けはしません。

    METAFILEPICT構造体は、HMETAFILEをメンバにしかないので拡張メタファイルを使用できません。
    また、CF_METAFILEPICTで要求されたとき、拡張メタファイルで返すようにしてみましたが、
    POWERPOINTの方でイメージが表示できません等のエラーメッセージが表示されました。

    POWERPOINTが、拡張メタファイルを要求してくれるように伝えればいいとは思うのですが。

    何か方法があれば教えてください。

    BOOL CMyAppSrvrItem::OnRenderData(LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium)
    {
    	if (lpFormatEtc->cfFormat == CF_METAFILEPICT)
    	{
    		// メタファイル
    		bret = GetMetafileData(lpFormatEtc, lpStgMedium);
    
    	}
    	else if (lpFormatEtc->cfFormat == CF_ENHMETAFILE)
    	{
    		// 拡張メタファイル
    		bret = GetEnhMetafileData(lpFormatEtc, lpStgMedium);
    	}
    
    	return bret;   // cfFormat not supported
    }
    

    2017年7月28日 5:26

回答

  • すみません。解決できました。

    自アプリがイメージを提供する形式の登録の順番を変えることで対応ができました。

    CMyAppSrvrItem::CMyAppSrvrItem (COleServerDoc* pContainerDoc)
    	: COleServerItem(pContainerDoc, TRUE)
    {
    	// 既に設定されているMetafilepict(WMF) を削除。
    	GetDataSource()->Empty();
    
    	// EMF を登録
    	FORMATETC formatEtc;
    	formatEtc.ptd = NULL;
    	formatEtc.dwAspect = DVASPECT_CONTENT;
    	formatEtc.lindex = -1;
    	formatEtc.cfFormat = CF_ENHMETAFILE;
    	formatEtc.tymed = TYMED_ENHMF;
    	GetDataSource()->DelayRenderData(0, &formatEtc);
    
    	// Metafilepict(WMF) を登録
    	FORMATETC formatEtc2;
    	formatEtc2.ptd = NULL;
    	formatEtc2.dwAspect = DVASPECT_CONTENT;
    	formatEtc2.lindex = -1;
    	formatEtc2.cfFormat = CF_METAFILEPICT;
    	formatEtc2.tymed = TYMED_MFPICT;
    	GetDataSource()->DelayRenderData(0, &formatEtc2);
    }
    
    

    • 回答の候補に設定 kenjinoteMVP 2017年8月1日 2:15
    • 回答としてマーク Brillia 2017年8月1日 3:18
    2017年8月1日 2:00

すべての返信

  • クリップボードには、複数のフォーマットでデータを設定できますが、
    クライアントがどの順番で、どのフォーマットから取得を試みるかはクライアント側の自由です。

    そもそも論として「取得してほしくないフォーマットは用意しない」のが
    サーバー側の姿勢ではないかと思うのです。

    クライアントがCF_METAFILEPICT形式のデータでの取得ができないことがわかれば、
    必要ならかつ可能ならCF_ENHMETAFILE形式での取得を試みるのではないかと予測できるのですが、どうなんでしょう。
    ただし、この取得順番はMSさんのお勧めではありませんので、パワポはそれを試みないかもしれません。

    2017年7月28日 6:20
  • もともと、CF_METAFILEPICT形式が要求されたとき、なにも処理せず、FALSEを返すことにしていました。

    確かにこの時は、OLE貼り付け時のイメージは文字化けしていませんでした。

    しかし、OLE編集後に要求されるのは、CF_METAFILEPICT形式のみだったため、
    編集後のイメージが更新されませんでした。

    そのため、CF_METAFILEPICT形式を対応するように戻したというのが現状です。


    2017年7月28日 6:43
  • メタファイルでの解決方法ですが、フォントの文字セットと文字列のコードページを指定した変換でどうにかなりませんか?

    // 韓国語の場合
    HFONT hFont = CreateFont(32, 0, 0, 0, 0, 0, 0, 0, HANGUL_CHARSET, 0, 0, 0, 0, 0);
    HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
    LPWSTR lpszKoreanW = L"안녕하세요"; // 韓国語
    int nLength = WideCharToMultiByte(949, 0, lpszKoreanW, -1, 0, 0, 0, 0);
    LPSTR lpszKoreanA = (LPSTR)GlobalAlloc(GPTR, nLength);
    WideCharToMultiByte(949, 0, lpszKoreanW, -1, lpszKoreanA, nLength, 0, 0);
    TextOutA(hdc, 0, 0, lpszKoreanA, lstrlenA(lpszKoreanA));
    GlobalFree(lpszKoreanA);
    SelectObject(hdc, hOldFont);
    DeleteObject(hFont);
    
    // 中国語(簡体字)の場合
    HFONT hFont = CreateFont(32, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, 0);
    HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
    LPWSTR lpszChineseW = L"您好"; // 中国語(簡体字)
    int nLength = WideCharToMultiByte(936, 0, lpszChineseW, -1, 0, 0, 0, 0);
    LPSTR lpszChineseA = (LPSTR)GlobalAlloc(GPTR, nLength);
    WideCharToMultiByte(936, 0, lpszChineseW, -1, lpszChineseA, nLength, 0, 0);
    TextOutA(hdc, 0, 0, lpszChineseA, lstrlenA(lpszChineseA));
    GlobalFree(lpszChineseA);
    SelectObject(hdc, hOldFont);
    DeleteObject(hFont);
    2017年7月28日 6:50
  • kenjinoteさんの解決方法を試そうと、文字コードを判別するものを作成してみましたが、
    次のコードでは「932」しか返ってきません。

    文字コードを判別できる方法ありますか?

    	MultiLanguage::tagDetectEncodingInfo encoding[10];
    	int Scores = 10;
    	::CoInitialize(NULL);
    	{
    		MultiLanguage::IMultiLanguage2Ptr p;
    		HRESULT hr = p.CreateInstance(__uuidof(MultiLanguage::CMultiLanguage));
    
    		wchar_t str[20];
    		//lstrcpy(str,_T("あいうえお"));
    		lstrcpy(str,_T("안녕하세요"));
    		//lstrcpy(str,_T("您好"));
    		int BufferSize = sizeof(str);
    
    		if (SUCCEEDED(hr)) {
    			p->DetectInputCodepage(MLDETECTCP_NONE ,0,(char*)str, &BufferSize, encoding,&Scores);
    		}
    	}
    	::CoUninitialize();
    
    
    	for (int i = 0; i < Scores; ++i)
    	{
    		if (encoding[i].nLangID != 0)
    		{
    			CString s;
    			s.Format(_T("CodePage=%d,DocPercen=%d"), encoding[i].nCodePage, encoding[i].nDocPercent);
    			MessageBox(s,_T(""),MB_OK);
    		}
      }
    

    2017年7月28日 7:45
  • 上記の私のプログラムを試すためにコードページの判別は必要でしたでしょうか?当初のご質問内容と変わっていますので、もしかすると新たにご質問されたほうが回答が集まりやすいかもしれません。

    DetectInputCodepage 関数について検証はしてませんが、恐らく引数の型を見ると、第三引数には Unicode 文字列ではなく、特定のコードページのマルチバイト文字列を指定するのではないかと思われます。

    Unicode 文字列の言語判定について、誤判定なく完全に動作するものを私は知りません。例えば、コードページを判別するために下記のようなコードを書いてみましたが、完全ではないようです。

    BOOL CheckCodePage(LPCWSTR lpszStringW, int nCodePage)
    {
    	int nLength = WideCharToMultiByte(nCodePage, 0, lpszStringW, -1, 0, 0, 0, 0);
    	LPSTR lpszStringA = (LPSTR)GlobalAlloc(GPTR, nLength);
    	WideCharToMultiByte(nCodePage, 0, lpszStringW, -1, lpszStringA, nLength, 0, 0);
    
    	nLength = MultiByteToWideChar(nCodePage, 0, lpszStringA, -1, 0, 0);
    	LPWSTR lpszNewStringW = (LPWSTR)GlobalAlloc(GPTR, nLength * sizeof(WCHAR));
    	MultiByteToWideChar(nCodePage, 0, lpszStringA, -1, lpszNewStringW, nLength);
    
    	BOOL bReturnValue = (lstrcmpW(lpszStringW, lpszNewStringW) == 0);
    
    	GlobalFree(lpszNewStringW);
    	GlobalFree(lpszStringA);
    
    	return bReturnValue;
    }
    
    CheckCodePage(L"あいうえお", 932) → TRUE
    CheckCodePage(L"あいうえお", 949) → TRUE
    CheckCodePage(L"あいうえお", 936) → TRUE
    
    CheckCodePage(L"안녕하세요", 932) → FALSE
    CheckCodePage(L"안녕하세요", 949) → TRUE
    CheckCodePage(L"안녕하세요", 936) → FALSE
    
    CheckCodePage(L"您好", 932) → FALSE
    CheckCodePage(L"您好", 949) → FALSE
    CheckCodePage(L"您好", 936) → TRUE


    日本語の文字列を指定すると、コードページ 932、949、936 すべてで TRUE となってしまっています。判定の順番を考えたり、ひらがなカタカナを含む場合の例外処理を考えるなど、工夫することで判定の精度を高められるかもしれません。

    2017年7月29日 4:36
  • 本質的な問題として、POWERPOINTがイメージ表示に拡張メタファイル形式を要求してくれるといいのですが。。。
    2017年7月31日 0:59
  • すみません。質問です。

    EXCEL,WORD,VISIOは、拡張メタファイルを要求してくるので、文字化けが起こりません。

    上記の現象を再現できませんでした。
    https://msdn.microsoft.com/ja-jp/library/8c47csfz(v=vs.80).aspx

    上記のサンプルを元に、OLE の貼り付けを行ってみたのですが、Word 2013、Excel 2013、Power Point 2013 のすべてで、OLE の編集時以外のとき CF_ENHMETAFILE を要求してくることはありませんでした。すべて CF_METAFILEPICT を要求してきているようです。Unicode 文字で描かれた文字列は、貼り付け直後文字化けがおこります。(※ OLE 編集時は Unicode 文字列の文字化けは起こりません。)

    編集時以外のとき CF_ENHMETAFILE が要求されるようにするために何かされていることがあるのでしょうか?できれば、こちらで現象を再現を確認し PowerPoint で何が起こっているか確認したいと思っております。こちらで再現可能な最小限のコードを提示していただくことは可能でしょうか。

    2017年7月31日 11:08
  • すみません。解決できました。

    自アプリがイメージを提供する形式の登録の順番を変えることで対応ができました。

    CMyAppSrvrItem::CMyAppSrvrItem (COleServerDoc* pContainerDoc)
    	: COleServerItem(pContainerDoc, TRUE)
    {
    	// 既に設定されているMetafilepict(WMF) を削除。
    	GetDataSource()->Empty();
    
    	// EMF を登録
    	FORMATETC formatEtc;
    	formatEtc.ptd = NULL;
    	formatEtc.dwAspect = DVASPECT_CONTENT;
    	formatEtc.lindex = -1;
    	formatEtc.cfFormat = CF_ENHMETAFILE;
    	formatEtc.tymed = TYMED_ENHMF;
    	GetDataSource()->DelayRenderData(0, &formatEtc);
    
    	// Metafilepict(WMF) を登録
    	FORMATETC formatEtc2;
    	formatEtc2.ptd = NULL;
    	formatEtc2.dwAspect = DVASPECT_CONTENT;
    	formatEtc2.lindex = -1;
    	formatEtc2.cfFormat = CF_METAFILEPICT;
    	formatEtc2.tymed = TYMED_MFPICT;
    	GetDataSource()->DelayRenderData(0, &formatEtc2);
    }
    
    

    • 回答の候補に設定 kenjinoteMVP 2017年8月1日 2:15
    • 回答としてマーク Brillia 2017年8月1日 3:18
    2017年8月1日 2:00
  • 本題からは外れてしまいますが:

    CheckCodePage(L"あいうえお", 932) → TRUE
    CheckCodePage(L"あいうえお", 949) → TRUE
    CheckCodePage(L"あいうえお", 936) → TRUE

    日本語の「平仮名」は、CJK(日中韓)の文字集合のいずれにも含まれてます。

    "あいうえお" の Unicode 表現
     U+3042 HIRAGANA LETTER A
     U+3044 HIRAGANA LETTER I
     U+3046 HIRAGANA LETTER U
     U+3048 HIRAGANA LETTER E
     U+304A HIRAGANA LETTER O

    "あいうえお" の CP932(日本語) 表現
     0x82A0 HIRAGANA LETTER A
     0x82A2 HIRAGANA LETTER I
     0x82A4 HIRAGANA LETTER U
     0x82A6 HIRAGANA LETTER E
     0x82A8 HIRAGANA LETTER O

    "あいうえお" の CP949(韓国語) 表現
     0xAAA2 HIRAGANA LETTER A
     0xAAA4 HIRAGANA LETTER I
     0xAAA6 HIRAGANA LETTER U
     0xAAA8 HIRAGANA LETTER E
     0xAAAA HIRAGANA LETTER O

    "あいうえお" の CP936(简体字中国語) 表現
     0xA4A2 HIRAGANA LETTER A
     0xA4A4 HIRAGANA LETTER I
     0xA4A6 HIRAGANA LETTER U
     0xA4A8 HIRAGANA LETTER E
     0xA4AA HIRAGANA LETTER O

    "您好" と "안녕하세요" はこんな感じ。

    "您好" の Unicode 表現
     U+6048 honorific for 'you'
     U+597D good, excellent, fine; well

    "您好" の CP932(日本語) 表現
     未定義
     0x8D44 CJK UNIFIED IDEOGRAPH

    "您好" の CP949(韓国語) 表現
     未定義
     0xFBBF CJK UNIFIED IDEOGRAPH

    "您好" の CP936 表現
     0x9063 CJK UNIFIED IDEOGRAPH
     0xBAC3 CJK UNIFIED IDEOGRAPH

    "안녕하세요" の Unicode 表現
     U+C548 HANGUL SYLLABLE IEUNG A NIEUN
     U+B155 HANGUL SYLLABLE NIEUN YEO IEUNG
     U+D558 HANGUL SYLLABLE HIEUH A
     U+C138 HANGUL SYLLABLE SIOS E
     U+C694 HANGUL SYLLABLE IEUNG YO
             
    "안녕하세요" の CP932(日本語) 表現
     未定義
     未定義
     未定義
     未定義

    "안녕하세요" の CP949(韓国語) 表現
     0xBEC8 HANGUL SYLLABLE IEUNG A NIEUN
     0xB3E7 HANGUL SYLLABLE NIEUN YEO IEUNG
     0xC7CF HANGUL SYLLABLE HIEUH A
     0xBCBC HANGUL SYLLABLE SIOS E
     0xBFE4 HANGUL SYLLABLE IEUNG YO

    "안녕하세요" の CP936(简体字中国語) 表現
     未定義
     未定義
     未定義
     未定義
    2017年8月1日 5:31