コード例 #1
0
        /// <summary>
        /// コンストラクタ1
        /// </summary>
        /// <param name="fileMapName">
        /// FileMapのオブジェクト名
        /// </param>
        /// <param name="maxByteSize">
        /// FileMapのバイト数(0を指定すると、全体を対象)
        /// ※ uintなので最大、4.294967295GBまで指定可能。
        /// </param>
        /// <param name="updateLockName">
        /// 更新ロックを行うMutexの名前
        /// </param>
        public SharedMemory(string fileMapName, uint maxByteSize, string updateLockName)
        {
            // メンバに設定

            // 各種設定を保持
            this._fileMapName        = fileMapName;
            this._maxFileMapByteSize = maxByteSize;
            this._updateLockName     = updateLockName;

            this._mappedViewPointer = IntPtr.Zero; // 初期化

            // FileMapを開く。
            this._fileMapHandle = MMapFileWin32.OpenFileMapping(
                MMapFileWin32.FileMapAccess.FileMapAllAccess, false, this.FileMapName);

            // 戻り値のチェック
            if (this.FileMapHandle == IntPtr.Zero)
            {
                // 開けなかった場合、

                // エラーコードをチェック
                if (CmnWin32.ErrorCodes.ERROR_FILE_NOT_FOUND == CmnWin32.GetLastError())
                {
                    // ファイルが存在しない場合、生成する。
                    this._fileMapHandle = MMapFileWin32.CreateFileMapping(
                        //IntPtr.Zero, IntPtr.Zero,
                        new IntPtr(-1), IntPtr.Zero,
                        MMapFileWin32.FileMapProtection.PageReadWrite
                        | MMapFileWin32.FileMapProtection.SectionCommit,
                        0, this.MaxFileMapByteSize, this.FileMapName);
                }
                else
                {
                    // 戻り値のチェック
                    if (this.FileMapHandle == IntPtr.Zero)
                    {
                        // 生成できなかった場合

                        this.Dispose(); // GC前にクリーンナップ

                        throw new WindowsAPIErrorException(
                                  CmnWin32.GetLastError(), string.Format(
                                      WindowsAPIErrorException.MessageTemplate, "CreateFileMapping"));
                    }
                    else
                    {
                        // 生成できた場合
                    }
                }
            }
            else
            {
                // 開けた場合
            }
        }
コード例 #2
0
        /// <summary>アンマネージ リソースをクリーンナップ</summary>
        /// <remarks>
        /// こちらは、ユーザ、デストラクタ(Finalizeメソッド)
        /// の双方が実行するClose、Disposeメソッドから実行される。
        /// </remarks>
        private void DisposeUnManagedResources()
        {
            // MapViewのアンマップ
            this.Unmap();

            // FileMapのハンドラをクローズする。
            if (this._fileMapHandle != IntPtr.Zero)
            {
                CmnWin32.CloseHandle(this.FileMapHandle);
                this._fileMapHandle = IntPtr.Zero;
            }
        }
コード例 #3
0
        /// <summary>
        /// FileMapをメモリ空間にマップし、MapViewを取得する。
        /// </summary>
        /// <param name="offset">
        /// FileMapの下位オフセット(32bitに制限)
        /// ※ uintなので最大、4.294967295GBまで指定可能。
        /// </param>
        /// <param name="mapViewByteSize">
        /// FileMapのバイト数(0を指定すると、全体を対象)
        /// ※ uintなので最大、4.294967295GBまで指定可能。
        /// </param>
        public void Map(uint offset, uint mapViewByteSize)
        {
            // チェック
            if (this.IsDisposed)
            {
                throw new ObjectDisposedException("SharedMemory");//, "Dispose済み。");
            }

            // offsetHighは設定しない(32bitに制限するため)。
            uint offsetHigh = 0;
            uint offsetLow  = offset;

            // マイナス値や、FileMapのサイズを超える場合は、FileMapのサイズに合わせる
            if (mapViewByteSize < 0 || (this.MaxFileMapByteSize < mapViewByteSize))
            {
                this._currentMapViewByteSize = this.MaxFileMapByteSize;
            }

            // 既にマップされている場合は、
            if (this._mappedViewPointer != IntPtr.Zero)
            {
                // 一度アンマップしてから、
                this.Unmap();
            }
            // マップしなおす(↓)。

            // FileMapをメモリ空間にマップし、
            // MapViewを取得する(MapViewのアドレスを返す)。
            this._mappedViewPointer
                = MMapFileWin32.MapViewOfFile(this.FileMapHandle,
                                              MMapFileWin32.FileMapAccess.FileMapAllAccess,
                                              offsetHigh, offsetLow, this.CurrentMapViewByteSize);

            // 0を指定した際の仕様に合わせて
            if (this._currentMapViewByteSize == 0)
            {
                this._currentMapViewByteSize = this.MaxFileMapByteSize;
            }

            // MapViewの取得エラー
            if (this.MappedViewPointer == IntPtr.Zero)
            {
                this.Dispose(); // GC前にクリーンナップ

                throw new WindowsAPIErrorException(
                          CmnWin32.GetLastError(), string.Format(
                              WindowsAPIErrorException.MessageTemplate, "MapViewOfFile"));
            }
        }
コード例 #4
0
        /// <summary>Informationエントリとしてメッセージを出力</summary>
        /// <param name="message">メッセージ</param>
        /// <param name="category">カテゴリ</param>
        /// <param name="eventID">eventID</param>
        public void Write(string message, ushort category, int eventID)
        {
            bool ret = false;
            // イベント・ソースの登録済みハンドルを開く
            IntPtr hEventLog = EventLogWin32.RegisterEventSource(null, APP_NAME);

            // ここでエラー(ERROR_ACCESS_DENIED )になる。
            // Writing in Security log on WinXP - Sysinternals Forums
            // http://forum.sysinternals.com/writing-in-security-log-on-winxp_topic2804.html
            CmnWin32.ErrorCodes ec = CmnWin32.GetLastError();

            // セキュリティ・ログに書き込み
            ret = EventLogWin32.ReportEvent(
                hEventLog, EVENTLOG_INFORMATION_TYPE, category, eventID, IntPtr.Zero, 1, 0, new string[] { message }, IntPtr.Zero);

            // イベント・ソースの登録済みハンドルを閉じる
            ret = EventLogWin32.DeregisterEventSource(hEventLog);
        }
コード例 #5
0
        /// <summary>測定終了メソッド</summary>
        /// <returns>処理が成功した場合:結果文字列、失敗した場合:エラーメッセージ</returns>
        /// <remarks>自由に利用できる。</remarks>
        public string EndsPerformanceRecord()
        {
#if NETSTD
            this._stopwatch.Stop();

            this._ExecTime = this._stopwatch.ElapsedMilliseconds.ToString();

            return
                ("ExT:" + this._ExecTime + "[msec]" +
                 ", CT: - [msec]" +
                 ", KT: - [msec]" +
                 ", UT: - [msec]");
#else
            try
            {
                #region 実行時間取得処理セクション

                // システム時刻(End)
                UInt64 u64ExTE = 0;

                long lngExTE = 0;

                if (QPCounterWin32.QueryPerformanceCounter(ref lngExTE) != 0)   // 時間の計測を開始します。
                {
                    // 周波数(更新頻度)を取得
                    long lngFreq = 0;
                    QPCounterWin32.QueryPerformanceFrequency(ref lngFreq);

                    // 秒単位
                    double dblTemp = ((lngExTE - LngExTS) * 1.0 / lngFreq);
                    // 1s → 100(ns)に合わせる。
                    dblTemp = dblTemp * 1000 * 1000 * 10;
                    // 整数値に変更
                    u64ExTE = (UInt64)Math.Round(dblTemp);
                }
                else// 高分解能のカウンタはサポートされません。
                {
                    // 100ns間隔(精度は低い)
                    u64ExTE = Convert.ToUInt64(DateTime.Now.Ticks);
                }

                #endregion

                #region CPU時間取得処理セクション

                // カレントスレッドのハンドルを返す(IDではないので注意)。
                IntPtr iptHdr;
                iptHdr = CmnWin32.GetCurrentThread();

                // スレッドの作成時刻
                System.Runtime.InteropServices.ComTypes.FILETIME ftCreateTime;
                // スレッドの終了時刻
                System.Runtime.InteropServices.ComTypes.FILETIME ftDELETETime;
                // カーネル時間
                System.Runtime.InteropServices.ComTypes.FILETIME ftKernelTime;
                // ユーザ時間
                System.Runtime.InteropServices.ComTypes.FILETIME ftUserTime;

                // 初期化が必要?
                ftCreateTime.dwHighDateTime = 0;
                ftCreateTime.dwLowDateTime  = 0;
                ftDELETETime.dwHighDateTime = 0;
                ftDELETETime.dwLowDateTime  = 0;
                ftKernelTime.dwHighDateTime = 0;
                ftKernelTime.dwLowDateTime  = 0;
                ftUserTime.dwHighDateTime   = 0;
                ftUserTime.dwLowDateTime    = 0;

                // Win32 API関数(GetCurrentThread)
                QPCounterWin32.GetThreadTimes(iptHdr, ref ftCreateTime, ref ftDELETETime,
                                              ref ftKernelTime, ref ftUserTime);

                // 計算用の領域
                UInt32 u32KTH;
                UInt32 u32KTL;
                UInt32 u32UTH;
                UInt32 u32UTL;

                // 変換(int32 ⇒ uint32)
                u32KTH = Convert.ToUInt32(ftKernelTime.dwHighDateTime);
                u32KTL = Convert.ToUInt32(ftKernelTime.dwLowDateTime);
                u32UTH = Convert.ToUInt32(ftUserTime.dwHighDateTime);
                u32UTL = Convert.ToUInt32(ftUserTime.dwLowDateTime);

                // CPU時間:(uint64 * uint32) + uint64 ⇒ uint64(オーバーフローはしない)
                // カーネル時間(End)
                UInt64 u64KTE;
                u64KTE = Convert.ToUInt64((u32KTH * U64HIBIT) + u32KTL);
                // ユーザ時間(End)
                UInt64 u64UTE;
                u64UTE = Convert.ToUInt64((u32UTH * U64HIBIT) + u32UTL);

                #endregion

                #region 出力文字列作成セクション

                // 当該処理の実行時間を算出
                UInt64 u64ExT;
                u64ExT = u64ExTE - U64ExTS;

                // 当該処理のCPU(カーネル)時間を算出
                UInt64 u64KT;
                u64KT = u64KTE - U64KTS;

                // 当該処理のCPU(ユーザ)時間を算出
                UInt64 u64UT;
                u64UT = u64UTE - U64UTS;

                // 当該処理のCPU時間を算出
                // ※ オーバーフローは無いはず...
                UInt64 u64CT;
                u64CT = u64KT + u64UT;

                // 初期化
                U64ExTS = 0;
                U64KTS  = 0;
                U64UTS  = 0;

                // 測定結果を文字列で返す
                double temp;

                // 四捨五入(msecの整数)
                temp           = Math.Floor((u64ExT * 0.1 * 0.001) + 0.5);
                this._ExecTime = temp.ToString();

                temp          = Math.Floor((u64CT * 0.1 * 0.001) + 0.5);
                this._CpuTime = temp.ToString();

                temp = Math.Floor((u64KT * 0.1 * 0.001) + 0.5);
                this._CpuKernelTime = temp.ToString();

                temp = Math.Floor((u64UT * 0.1 * 0.001) + 0.5);
                this._CpuUserTime = temp.ToString();

                return
                    ("ExT:" + this._ExecTime + "[msec]" +
                     ", CT:" + this._CpuTime + "[msec]" +
                     ", KT:" + this._CpuKernelTime + "[msec]" +
                     ", UT:" + this._CpuUserTime + "[msec]");

                #endregion
            }

            catch (Exception ex)
            {
                // ランタイムエラー。
                return(ex.Message);
            }
#endif
        }
コード例 #6
0
        /// <summary>測定開始メソッド</summary>
        /// <returns>処理が成功した場合:True、失敗した場合:False</returns>
        /// <remarks>自由に利用できる。</remarks>
        public bool StartsPerformanceRecord()
        {
#if NETSTD
            this._stopwatch = new Stopwatch();
            this._stopwatch.Start();
#else
            #region メンバ変数を初期化する

            // 実行時間
            this.U64ExTS = 0;
            this.LngExTS = 0; // QueryPerformanceCounter用

            // CPU時間
            this.U64KTS = 0;
            this.U64UTS = 0;

            // 測定結果の保存用メンバ変数の初期化
            this._ExecTime      = "";
            this._CpuTime       = "";
            this._CpuKernelTime = "";
            this._CpuUserTime   = "";

            #endregion

            try
            {
                #region 実行時間取得処理セクション

                // システム時刻(Start)

                if (QPCounterWin32.QueryPerformanceCounter(ref LngExTS) != 0)   // 時間の計測を開始します。
                {
                }
                else// 高分解能のカウンタはサポートされません。
                {
                    // 100ns間隔(精度は低い)
                    U64ExTS = Convert.ToUInt64(DateTime.Now.Ticks);
                }

                #endregion

                #region CPU時間取得処理セクション

                // カレントスレッドのハンドルを返す(IDではないので注意)。
                IntPtr iptHdr;
                iptHdr = CmnWin32.GetCurrentThread();

                // スレッドの作成時刻
                System.Runtime.InteropServices.ComTypes.FILETIME ftCreateTime;
                // スレッドの終了時刻
                System.Runtime.InteropServices.ComTypes.FILETIME ftDELETETime;
                // カーネル時間
                System.Runtime.InteropServices.ComTypes.FILETIME ftKernelTime;
                // ユーザ時間
                System.Runtime.InteropServices.ComTypes.FILETIME ftUserTime;

                // 初期化が必要?
                ftCreateTime.dwHighDateTime = 0;
                ftCreateTime.dwLowDateTime  = 0;
                ftDELETETime.dwHighDateTime = 0;
                ftDELETETime.dwLowDateTime  = 0;
                ftKernelTime.dwHighDateTime = 0;
                ftKernelTime.dwLowDateTime  = 0;
                ftUserTime.dwHighDateTime   = 0;
                ftUserTime.dwLowDateTime    = 0;

                // Win32 API関数(GetCurrentThread)
                QPCounterWin32.GetThreadTimes(iptHdr, ref ftCreateTime, ref ftDELETETime,
                                              ref ftKernelTime, ref ftUserTime);

                // 計算用の領域
                UInt32 u32KTH;
                UInt32 u32KTL;
                UInt32 u32UTH;
                UInt32 u32UTL;

                // 変換(int32 ⇒ uint32)
                u32KTH = Convert.ToUInt32(ftKernelTime.dwHighDateTime);
                u32KTL = Convert.ToUInt32(ftKernelTime.dwLowDateTime);
                u32UTH = Convert.ToUInt32(ftUserTime.dwHighDateTime);
                u32UTL = Convert.ToUInt32(ftUserTime.dwLowDateTime);

                // CPU時間:(uint64 * uint32) + uint64 ⇒ uint64(オーバーフローはしない)

                // カーネル時間(Start)
                U64KTS = Convert.ToUInt64((u32KTH * U64HIBIT) + u32KTL);

                // ユーザ時間(Start)
                U64UTS = Convert.ToUInt64((u32UTH * U64HIBIT) + u32UTL);

                #endregion
            }
            catch
            {
                // ランタイムエラー。
                return(false);
            }
            finally
            {
            }
#endif
            return(true);
        }
コード例 #7
0
        /// <summary>
        /// ASP.NET で偽装ユーザーのコンテキストで実行されるプロセスを生成する
        /// http://support.microsoft.com/kb/889251/ja
        /// </summary>
        /// <param name="commandLinePath">コマンドライン</param>
        /// <param name="currentDirectory">カレント・ディレクトリ</param>
        /// <param name="impersonationLevel">偽装レベル</param>
        /// <param name="errorInfo">エラー情報</param>
        /// <returns>
        /// ・true:成功
        /// ・false:失敗
        /// </returns>
        public static bool CreateProcessAsImpersonationUser(
            string commandLinePath, string currentDirectory,
            SecurityWin32.SECURITY_IMPERSONATION_LEVEL impersonationLevel, out string errorInfo)
        {
            // エラー情報の初期化
            errorInfo = "";

            // 失敗するので初期化
            if (string.IsNullOrEmpty(currentDirectory))
            {
                currentDirectory =
                    Environment.GetEnvironmentVariable(
                        "SystemRoot", EnvironmentVariableTarget.Process);
            }

            // トークン
            IntPtr token = IntPtr.Zero;
            // 継承可能にする。
            IntPtr tokenDuplicate = IntPtr.Zero;

            // 戻り値
            bool ret;

            // 偽装ユーザのアカウント・トークン
            token = WindowsIdentity.GetCurrent().Token;

            // SECURITY_ATTRIBUTES構造体
            SecurityWin32.SECURITY_ATTRIBUTES sa
                = new SecurityWin32.SECURITY_ATTRIBUTES();

            // Security Descriptor
            sa.lpSecurityDescriptor = IntPtr.Zero; // = (IntPtr)0;
            // Security Descriptorのハンドルは継承不可能
            sa.bInheritHandle = false;
            // サイズ
            sa.Length = Marshal.SizeOf(sa);

            try
            {
                // 偽装アクセストークンハンドルは、
                // CreateProcessAsUserに指定できないため、
                // DuplicateTokenExでプライマリ・トークンに変換する
                ret = SecurityWin32.DuplicateTokenEx(
                    token, (uint)SecurityWin32.ACCESS_MASK.GENERIC_ALL, ref sa,
                    (int)impersonationLevel, (int)SecurityWin32.TOKEN_TYPE.TokenPrimary, ref tokenDuplicate);

                // 戻り値判定
                if (ret)
                {
                    // true(成功)

                    // STARTUPINFO構造体
                    ProcessWin32.STARTUPINFO si = new ProcessWin32.STARTUPINFO();
                    // デスクトップ名
                    si.lpDesktop = "";
                    // サイズ
                    si.cb = Marshal.SizeOf(si);

                    // PROCESS_INFORMATION構造体
                    ProcessWin32.PROCESS_INFORMATION pi = new ProcessWin32.PROCESS_INFORMATION();

                    // 偽装可能にしたトークンを指定してプロセス起動
                    ret = ProcessWin32.CreateProcessAsUser(
                        tokenDuplicate, null, commandLinePath, ref sa, ref sa, false,
                        0, IntPtr.Zero, currentDirectory, ref si, out pi);

                    // 戻り値判定
                    if (ret)
                    {
                        // true(成功)

                        CmnWin32.CloseHandle(pi.hProcess);
                        CmnWin32.CloseHandle(pi.hThread);

                        // 偽装可能にしたトークンのハンドラを閉じる
                        ret = CmnWin32.CloseHandle(tokenDuplicate);
                    }
                    else
                    {
                        // asp.net - Running cscript.exe from C# .ashx does not execute code in vbscript file - Stack Overflow
                        // http://stackoverflow.com/questions/3842020/running-cscript-exe-from-c-sharp-ashx-does-not-execute-code-in-vbscript-file

                        // false(失敗)
                        errorInfo = "CreateProcessAsUser failed with " + Marshal.GetLastWin32Error()
                                    + ": if 1314, make sure user is a member 'Replace a process level token' Control Panel -> Administrative Tools -> Local Security Settings.";
                    }
                }
                else
                {
                    // false(失敗)
                    errorInfo = "DuplicateTokenEx failed with " + Marshal.GetLastWin32Error();
                }
            }
            finally
            {
                // 失敗(例外発生)時など。

                // トークンハンドル、
                // 偽装アクセストークンハンドルをクローズ
                if (token != IntPtr.Zero)
                {
                    CmnWin32.CloseHandle(token);
                }
                if (tokenDuplicate != IntPtr.Zero)
                {
                    CmnWin32.CloseHandle(tokenDuplicate);
                }
            }

            // false(失敗)
            return(false);
        }
コード例 #8
0
        /// <summary>
        /// ユーザ名・ドメイン・パスワードで偽装する。
        /// </summary>
        /// <param name="userName">ユーザ名</param>
        /// <param name="domain">ドメイン</param>
        /// <param name="password">パスワード</param>
        /// <param name="impersonationLevel">偽装レベル</param>
        /// <param name="errorInfo">エラー情報</param>
        /// <returns>
        /// ・true:成功
        /// ・false:失敗
        /// </returns>
        public bool ImpersonateValidUser(
            string userName, string domain, string password,
            SecurityWin32.SECURITY_IMPERSONATION_LEVEL impersonationLevel, out string errorInfo)
        {
            // エラー情報の初期化
            errorInfo = "";

            // ワーク
            WindowsIdentity tempWindowsIdentity;

            // トークンのハンドラ
            IntPtr token          = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            try
            {
                // クライアントアプリケーションによる偽装を終了。
                if (SecurityWin32.RevertToSelf())
                {
                    // RevertToSelf成功

                    // 偽装する。

                    // トークンハンドルを取得
                    if (SecurityWin32.LogonUserA(userName, domain, password,
                                                 LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
                    {
                        // LogonUserA成功

                        // 偽装アクセストークンハンドルを作成
                        if (SecurityWin32.DuplicateToken(token, impersonationLevel, ref tokenDuplicate) != 0)
                        {
                            // DuplicateToken成功

                            // 偽装アクセストークンを使用して偽装する。
                            tempWindowsIdentity       = new WindowsIdentity(tokenDuplicate);
                            this.impersonationContext = tempWindowsIdentity.Impersonate();
                            if (this.impersonationContext != null)
                            {
                                // Impersonate成功
                                // 正常終了
                                return(true);
                            }
                            else
                            {
                                // Impersonate失敗
                                errorInfo = "Impersonate failed";
                            }
                        }
                        else
                        {
                            // DuplicateToken失敗
                            errorInfo = "DuplicateToken failed with " + Marshal.GetLastWin32Error();
                        }
                    }
                    else
                    {
                        // LogonUserA失敗
                        errorInfo = "LogonUserA failed with " + Marshal.GetLastWin32Error();
                    }
                }
                else
                {
                    // RevertToSelf失敗
                    errorInfo = "RevertToSelf failed with " + Marshal.GetLastWin32Error();
                }
            }
            finally
            {
                // 失敗(例外発生)時など。

                // トークンハンドル、
                // 偽装アクセストークンハンドルをクローズ
                if (token != IntPtr.Zero)
                {
                    CmnWin32.CloseHandle(token);
                }
                if (tokenDuplicate != IntPtr.Zero)
                {
                    CmnWin32.CloseHandle(tokenDuplicate);
                }
            }

            // 異常終了
            return(false);
        }
コード例 #9
0
        /// <summary>クライアント生成 - Writeスレッド関数</summary>
        private void WriteSharedMemory()
        {
            // 共有メモリ(サーバ)
            SharedMemory sm = null;

            // スレッドID
            int managedThreadId = Thread.CurrentThread.ManagedThreadId;

            try
            {
                // 共有メモリを生成(256バイト)
                sm = new SharedMemory("my-sm", 256, "my-mtx");

                // マップ
                sm.Map(0, 0);
                // ロック
                sm.Lock();

                // システム時間、ローカル時間の「Manage SYSTEMTIME構造体」
                SYSTEMTIME[] csts = new SYSTEMTIME[2];

                // システム時間
                CmnWin32.GetSystemTime(out csts[0]);
                string systemTime =
                    string.Format("{0:0000}/{1:00}/{2:00} {3:00}:{4:00}:{5:00}.{6:000}",
                                  csts[0].Year, csts[0].Month, csts[0].Day,
                                  csts[0].Hour, csts[0].Minute, csts[0].Second, csts[0].Milliseconds);

                // ローカル時間
                CmnWin32.GetLocalTime(out csts[1]);
                string localTime =
                    string.Format("{0:0000}/{1:00}/{2:00} {3:00}:{4:00}:{5:00}.{6:000}",
                                  csts[1].Year, csts[1].Month, csts[1].Day,
                                  csts[1].Hour, csts[1].Minute, csts[1].Second, csts[1].Milliseconds);

                // 共有メモリを初期化
                sm.SetMemory(CmnClass.InitBuff(256), 256);

                // マーシャリング(「Unmanage SYSTEMTIME構造体」のバイト表現を取得)

                //// (1)
                //SYSTEMTIME cst = new SYSTEMTIME();
                //int sizeCst = Marshal.SizeOf(cst);
                //byte[] cstBytes = new byte[sizeCst];
                //byte[] cstsBytes = new byte[sizeCst * 2];

                //Array.Copy(CustomMarshaler.StructureToBytes(csts[0]), 0, cstsBytes, 0, sizeCst);
                //Array.Copy(CustomMarshaler.StructureToBytes(csts[1]), 0, cstsBytes, sizeCst * 1, sizeCst);

                // (2)
                byte[] cstsBytes = CustomMarshaler.StructuresToBytes(new object[] { csts[0], csts[1] }, 2);

                // 共有メモリへ書き込む。
                sm.SetMemory(cstsBytes, cstsBytes.Length);

                // 送信メッセージを表示
                this.SetResult_Client(
                    string.Format("({0})送信:{1}", managedThreadId,
                                  "\r\nsystemTime:" + systemTime + "\r\nlocalTime:" + localTime));
            }
            catch (Exception ex)
            {
                // エラーを表示
                this.SetResult_Client(
                    string.Format("({0})エラー:{1}", managedThreadId, ex.ToString()));
            }
            finally
            {
                if (sm != null)
                {
                    // 共有メモリをクローズ
                    // アンロック&マネージ・アンマネージリソースの解放
                    sm.Close();// ←コメントアウトするとGC任せになるが、ミューテックスの解放が遅れる!
                }
            }
        }