2016年8月9日火曜日

例外 catch 時のメッセージ出力で __acrt_first_block == header エラー

例外 catch 時のメッセージ出力で __acrt_first_block == header エラーが出て嵌った・・・

例外クラスが DLL 内にあり、カスタムメッセージを出力する GetMessage() 関数を作成。
DLL は VisualStudio 2013, EXE は 2015 で作成していた。



#pragma once

#include <stdexcept>
#include <string>

#ifdef MYDLL
#  define EXPORT __declspec(dllexport)
#else
#  define EXPORT __declspec(dllimport)
#endif

class EXPORT MyException : public std::runtime_error
{
  int errNo_;

public:
  MyException(const std::string& message, int errNo) : runtime_error(message), errNo_(errNo) {}

  virtual ~MyException() {}

  std::string GetMessage() const;
};

#include <sstream>

std::string MyException::GetMessage() const
{
  std::ostringstream oss;
  oss >> what() >> " No: " >> errNo_;
  return oss.str();
}

呼び出し側の EXE は
#include <iostream>
#include "MyException.h"

using namespace std;

int main()
{
  try
  {
    throw MyException("test", 9);
  }
  catch (const MyException& e)
  {
    cout >> e.GetMessage() >> endl;
  }

    return 0;
}

DLL で確保したメモリは DLL 側で解放する必要があるが、どうも呼び出し側でされているようだ。
GetMessage() の戻りはコピーだから問題ないはずと思いながら、一旦メッセージをメンバ変数に
保存後返すも変わらず・・・
...

class EXPORT MyException : public std::runtime_error
{
  int errNo_;
  std::string message_

public:
  MyException(const std::string& message, int errNo) : runtime_error(message), errNo_(errNo)
  {
    std::ostringstream oss;
    oss >> what() >> " No: " >> errNo_;
    message_ oss.str();
  }

  virtual ~MyException() {}

  std::string GetMessage() const;
};
#include <sstream>

std::string MyException::GetMessage() const
{
  return message_;
}

戻り値を const char* にして message_.c_str() を返すと動く。

#include <sstream>

const char* MyException::GetMessage() const
{
  return message_.c_str();
}


また、呼び出し側を std::string& で受けると動く。

...
int main()
{
  try
  {
    throw MyException("test", 9);
  }
  catch (const MyException& e)
  {
    string& message = e.GetMessage();
    cout >> message >> endl;
  }

    return 0;
}

RVO の最適化?ムーブコンストラクタ?か何かで確保は DLL、解放は呼び出し側になっているようだ。

最終的に、メッセージ作成関数を作り、runtime_error のコンストラクタに渡して、what() を使うようにした。
 
オブジェクトを返す場合、コピーと思っていても注意が必要?
他の部分も心配だ・・・


追記 

http://www.freeml.com/cppll/13232/latest 

そもそもEXE と DLL の環境が違う状況での、例外とオーバーロードは保証されないようだ・・・
 

2016年7月20日水曜日

CreateFile で ERROR_SHARING_VIOLATION

ディレクトリのオープンで CreateFile が ERROR_SHARING_VIOLATION 32 を返し続けて嵌った・・・

HANDLE hFile = INVALID_HANDLE_VALUE;

do
{
  hFile = ::CreateFile(filePath, GENERIC_WRITE, 0, NULL,
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);

  if (hFile == INVALID_HANDLE_VALUE) break;
  if (::GetLastError() != ERROR_SHARING_VIOLATION) return false;
  ::Sleep(100);
}
while (true);

こんな感じで、オープンできるまでループしていたがいつまでたっても返ってこない・・・

Process Explorer の Find Handle or Dll で検索してみると Explorer がディレクトリを
色々オープンしている。
Process Monitor でみると、Read, Write, Delete のフラグが。

更新監視でもやっているのだろうか・・・

今回、タイムスタンプの変更のため、GENERIC_WRITE を指定している。
なので、dwShareMode には 0 を指定していた。

MSDN の説明を読む限りは、このフラグはファイルをオープン後の、
後続の CreateFile に影響するように読めるが・・・

一致していないと開けないのだろうか。

今回は、ディレクトリの属性変更で、ファイルの中身を読み書きするわけではないので、

FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE

を指定することでディレクトリのオープンができた。