多重起動を防止するやつをパワーアップさせたい。

ActiveBasicでのプログラミングでわからないこと、困ったことなどがあったら、ここで質問してみましょう(質問を行う場合は、過去ログやWeb上であらかじめ問題を整理するようにしましょう☆)。
返信する
メッセージ
作成者
konisi
記事: 893
登録日時: 2005年7月25日(月) 13:27
お住まい: 埼玉県東松山市
連絡する:

多重起動を防止するやつをパワーアップさせたい。

#1 投稿記事 by konisi »

多重起動を防止する方法なら知っているのですが、多重起動できないソフトを起動させた状態で同一ソフトを起動させようとした時に元々起動していた方のソフトのウインドウをアクティブにするということはできないのでしょうか?

WinXPにてCtrl+Alt+Deleteを押すと出てくるタスクマネージャのような。
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。

に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
ノッチ
記事: 82
登録日時: 2005年6月01日(水) 23:27
お住まい: 北海道札幌市

Re: 多重起動を防止するやつをパワーアップさせたい。

#2 投稿記事 by ノッチ »

2重起動防止はFindWindowかミューテックスを使用していると思いますが、
他にファイルマッピングという方法もあります。

CreateFileMapping

というAPIを調べてみてください。
簡単にいうとメモリをプロセス間で共有するということです。
konisi
記事: 893
登録日時: 2005年7月25日(月) 13:27
お住まい: 埼玉県東松山市
連絡する:

#3 投稿記事 by konisi »

ありがとうございました。とりあえずBasicHelpに載ってないようなのでBackSearchABで探して、サンプルが無いのでググルさんに聞いたのですが・・・
http://www.microsoft.com/japan/develope ... apping.htm

コード: 全て選択


HANDLE CreateFileMapping(
  HANDLE hFile,                       // ファイルのハンドル
  LPSECURITY_ATTRIBUTES lpAttributes, // セキュリティ
  DWORD flProtect,                    // 保護
  DWORD dwMaximumSizeHigh,            // サイズを表す上位 DWORD
  DWORD dwMaximumSizeLow,             // サイズを表す下位 DWORD
  LPCTSTR lpName                      // オブジェクト名
);
C言語は読めるので移植する上で問題は無いのですが、SECURITY_ATTRIBUTES構造体がどのようなものか分かりません。誰か知ってる人いますか?

コード: 全て選択


'ActiveBasic調に直してみる。
Declare Function CriateFileMapping Lib "kernel32" (
  hFile As HANDLE,
  lpAttributes As LPSECURITY_ATTRIBUTES,
  flProtect As DWord,
  dwMaximumSizeHigh As DWord,
  dwMaximumSizeLow As DWord,
  lpName As BytePtr) As HANDLE
ちなみに、GetLastError()で183が返るかどうかで2重起動をチェックしてました。
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。

に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
hira
記事: 203
登録日時: 2005年5月31日(火) 20:14
お住まい: 兵庫県
連絡する:

Re: 多重起動を防止するやつをパワーアップさせたい。

#4 投稿記事 by hira »

多重起動できないソフトを起動させた状態で同一ソフトを起動させようとした時に元々起動していた方のソフトのウインドウをアクティブにする
こっちへのレスです(^^;

コード: 全て選択

Type PROCESSENTRY32
	dwSize As DWord
	cntUsage As DWord
	th32ProcessID As DWord
	th32DefaultHeapID As *DWord
	th32ModuleID As DWord
	cntThreads As DWord
	th32ParentProcessID As DWord
	pcPriClassBase As Long
	dwFlags As DWord
	szExeFile[ELM(MAX_PATH)] As Byte
End Type
Const TH32CS_SNAPPROCESS=2

Declare Function CreateToolhelp32Snapshot Lib "kernel32" (dwFlag As DWord,th32ProcessID As DWord) As HANDLE
Declare Function Process32First Lib "kernel32" (hSnapshot As HANDLE,ByRef lppe As PROCESSENTRY32) As Long
Declare Function Process32Next Lib "kernel32" (hSnapshot As HANDLE,ByRef lppe As PROCESSENTRY32) As Long
Declare Function EnumWindows Lib "user32" (lpEnumFunc As VoidPtr,lParam As LPARAM) As Long

Function EnumWindowsProc(hWnd As HWND,lParam As LPARAM) As Long
Dim dwProcessId As DWord
	'同じ名前のEXEの可視ウィンドウをすべてアクティブ表示
	GetWindowThreadProcessId(hWnd,VarPtr(dwProcessId))
	If dwProcessId=lParam And IsWindowVisible(hWnd)<>0 Then ShowWindow(hWnd,SW_SHOW)
	EnumWindowsProc=1
End Function

Dim hSnapHandle As HANDLE
Dim udtProcessEntry32 As PROCESSENTRY32
	'プロセスを列挙
	hSnapHandle=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0)
	With udtProcessEntry32
		.dwSize=SizeOf(PROCESSENTRY32)
		If Process32First(hSnapHandle,udtProcessEntry32) Then
			Do
				'自分と同じファイル名か?
				If lstrcmpi(.szExeFile,"test.exe")=0 Then
					'自分自身は対象から外す
					If .th32ProcessID<>GetCurrentProcessId() Then
						'ウィンドウ列挙
						EnumWindows(AddressOf(EnumWindowsProc),.th32ProcessID)
					End If
				End If
			Loop While Process32Next(hSnapHandle,udtProcessEntry32)
		End If
	End With
	CloseHandle(hSnapHandle)
例は test.exe という名前のEXEファイルだったときの処理です(適当に変えてください)。
簡単にしか実験していないので、ひょっとしたら動作がおかしいかもしれませんが、そのときは細かいところをいろいろ変更してみてください(^^;;
ノッチ
記事: 82
登録日時: 2005年6月01日(水) 23:27
お住まい: 北海道札幌市

#5 投稿記事 by ノッチ »

> C言語は読めるので移植する上で問題は無いのですが、SECURITY_ATTRIBUTES構造体がどのようなものか分かりません。誰か知ってる人いますか?
>

コード: 全て選択


> 'ActiveBasic調に直してみる。
> Declare Function CriateFileMapping Lib "kernel32" (
>   hFile As HANDLE,
>   lpAttributes As LPSECURITY_ATTRIBUTES,
>   flProtect As DWord,
>   dwMaximumSizeHigh As DWord,
>   dwMaximumSizeLow As DWord,
>   lpName As BytePtr) As HANDLE
> 

コード: 全て選択

Declare Function CreateFileMapping Lib "kernel32" Alias "CreateFileMappingA" (
  hFile As HANDLE,
  lpAttributes As *SECURITY_ATTRIBUTES,
  flProtect As DWord,
  dwMaximumSizeHigh As DWord,
  dwMaximumSizeLow As DWord,
  lpName As BytePtr) As HANDLE
AB風な宣言はこんな感じでしょうか。

SECURITY_ATTRIBUTES構造体はCreateFile等でも使用されるもので、
ハンドルを継承する(?)場合なんかで使われるらしいですが・・・。
よくわからないのでいつもNULL渡してます。
NULLの場合デフォルトの設定が使われるらしいので問題ないはずです。

ちなみにhiraさんのコードの方が大変そうですがより正確にできますね。
konisi
記事: 893
登録日時: 2005年7月25日(月) 13:27
お住まい: 埼玉県東松山市
連絡する:

なぜだろ。

#6 投稿記事 by konisi »

hiraさんのコードの先頭に#consoleと付け加えて一番下にSleep(1000000)と書き足して・・・
とりあえずテストしてみたのですが、普通に多重起動します・・・

コード: 全て選択

#console
Type PROCESSENTRY32
    dwSize As DWord
    cntUsage As DWord
    th32ProcessID As DWord
    th32DefaultHeapID As *DWord
    th32ModuleID As DWord
    cntThreads As DWord
    th32ParentProcessID As DWord
    pcPriClassBase As Long
    dwFlags As DWord
    szExeFile[ELM(MAX_PATH)] As Byte
End Type
Const TH32CS_SNAPPROCESS=2
Declare Function CreateToolhelp32Snapshot Lib "kernel32" (dwFlag As DWord,th32ProcessID As DWord) As HANDLE
Declare Function Process32First Lib "kernel32" (hSnapshot As HANDLE,ByRef lppe As PROCESSENTRY32) As Long
Declare Function Process32Next Lib "kernel32" (hSnapshot As HANDLE,ByRef lppe As PROCESSENTRY32) As Long
Declare Function EnumWindows Lib "user32" (lpEnumFunc As VoidPtr,lParam As LPARAM) As Long

Function EnumWindowsProc(hWnd As HWND,lParam As LPARAM) As Long
Dim dwProcessId As DWord
    '同じ名前のEXEの可視ウィンドウをすべてアクティブ表示
    GetWindowThreadProcessId(hWnd,VarPtr(dwProcessId))
    If dwProcessId=lParam And IsWindowVisible(hWnd)<>0 Then ShowWindow(hWnd,SW_SHOW)
    EnumWindowsProc=1
End Function

Dim hSnapHandle As HANDLE
Dim udtProcessEntry32 As PROCESSENTRY32
    'プロセスを列挙
    hSnapHandle=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0)
    With udtProcessEntry32
        .dwSize=SizeOf(PROCESSENTRY32)
        If Process32First(hSnapHandle,udtProcessEntry32) Then
            Do
                '自分と同じファイル名か?
                If lstrcmpi(.szExeFile,"test.exe")=0 Then
                    '自分自身は対象から外す
                    If .th32ProcessID<>GetCurrentProcessId() Then
                        'ウィンドウ列挙
                        EnumWindows(AddressOf(EnumWindowsProc),.th32ProcessID)
                    End If
                End If
            Loop While Process32Next(hSnapHandle,udtProcessEntry32)
        End If
    End With
    CloseHandle(hSnapHandle)
Sleep(1000000)
一応、#promptでもやってみましたが多重起動します。


スペック
OS:WindowsXP HomeEdition SP2
CPU:3GHz、デュアルコア
(デバイスマネージャの表記:Intel(R) Pentium(R) 4 CPU 3.00GHz)
メモリ:512MB
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。

に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
hira
記事: 203
登録日時: 2005年5月31日(火) 20:14
お住まい: 兵庫県
連絡する:

#8 投稿記事 by hira »

すみません、多重起動防止機能は省略して書いていました(^^;;
ミューテックスで判断しておけばいいかな、と思ったので…。
とりあえず、この検索処理を利用して多重起動防止をしようとすると

コード: 全て選択

Type PROCESSENTRY32
	dwSize As DWord
	cntUsage As DWord
	th32ProcessID As DWord
	th32DefaultHeapID As *DWord
	th32ModuleID As DWord
	cntThreads As DWord
	th32ParentProcessID As DWord
	pcPriClassBase As Long
	dwFlags As DWord
	szExeFile[ELM(MAX_PATH)] As Byte
End Type
Const TH32CS_SNAPPROCESS=2

Declare Function CreateToolhelp32Snapshot Lib "kernel32" (dwFlag As DWord,th32ProcessID As DWord) As HANDLE
Declare Function Process32First Lib "kernel32" (hSnapshot As HANDLE,ByRef lppe As PROCESSENTRY32) As Long
Declare Function Process32Next Lib "kernel32" (hSnapshot As HANDLE,ByRef lppe As PROCESSENTRY32) As Long
Declare Function EnumWindows Lib "user32" (lpEnumFunc As VoidPtr,lParam As LPARAM) As Long

Function EnumWindowsProc(hWnd As HWND,lParam As LPARAM) As Long
Dim dwProcessId As DWord
	GetWindowThreadProcessId(hWnd,VarPtr(dwProcessId))
	If dwProcessId=lParam And IsWindowVisible(hWnd)<>0 Then ShowWindow(hWnd,SW_RESTORE)
	EnumWindowsProc=1
End Function

Dim hSnapHandle As HANDLE
Dim udtProcessEntry32 As PROCESSENTRY32
	'プロセスを列挙
	hSnapHandle=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0)
	With udtProcessEntry32
		.dwSize=SizeOf(PROCESSENTRY32)
		If Process32First(hSnapHandle,udtProcessEntry32) Then
			Do
			    '自分と同じファイル名か?
			    If lstrcmpi(.szExeFile,"test.exe")=0 Then
			        '自分自身は対象から外す
			        If .th32ProcessID<>GetCurrentProcessId() Then
			            'ウィンドウ列挙
						EnumWindows(AddressOf(EnumWindowsProc),.th32ProcessID)
			            '自分自身を終了する
			            CloseHandle(hSnapHandle)
			            End
			        End If
			    End If
			Loop While Process32Next(hSnapHandle,udtProcessEntry32)
		End If
	End With
	CloseHandle(hSnapHandle)
です。
ケースケ
記事: 45
登録日時: 2005年5月31日(火) 23:46
お住まい: 愛知県
連絡する:

私の場合

#9 投稿記事 by ケースケ »

多重起動できないソフトを起動させた状態で同一ソフトを起動させようとした時に元々起動していた方のソフトのウインドウをアクティブにするということはできないのでしょうか?
というのを私の場合は、
ミューテックスで判断して多重起動していたらRegisterWindowMessageで何かメッセージを取得し、SendMessageで全てのウインドウに送っています。
メッセージループ内にRegisterWindowMessageのメッセージを調べる部分を作っておいて、最初から起動している方は強引にアクティブにします。
一方、あとから起動したほうは、メッセージを送信後に終了します。

私のソフトで使っている方法です。
konisi
記事: 893
登録日時: 2005年7月25日(月) 13:27
お住まい: 埼玉県東松山市
連絡する:

#10 投稿記事 by konisi »

hiraさんのコードで多重起動は解決しました。ありがとうございました。
ところで、どうやって元から起動していた実行ファイルの窓をアクティブにするかが分かりません。

システム系無知ですみません^^;


そもそも私はミューテックスがどのようなものか知らずにレス返してましたが、BackSearchABで調べたらGetLastError関数で確実に183(ERROR_ALREADY_EXISTS)が返るようにするものかなと解釈したところです。間違ってたら指摘してやってください。
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。

に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
NoWest
記事: 264
登録日時: 2005年5月31日(火) 10:52
お住まい: 高知
連絡する:

#11 投稿記事 by NoWest »

konisi さんが書きました:> hiraさんのコードで多重起動は解決しました。ありがとうございました。
> ところで、どうやって元から起動していた実行ファイルの窓をアクティブにするかが分かりません。
>
> システム系無知ですみません^^;
確かに、いきなりEnumWindows関数は難しいですね。
しかし、英語名からある程度意味は推測できると思います。

このEnumWindows関数は現在デスクトップ上で実行中のあらゆるプログラムのウィンドウハンドルを列挙、つまり簡単にいうとウィンドウを1つずつ順番にお目当てのウィンドウハンドルが得られるまで調べていくものです。

コード: 全て選択

Declare Function EnumWindows Lib "user32" (lpEnumFunc As VoidPtr,lParam As LPARAM) As Long

Function EnumWindowsProc(hWnd As HWND,lParam As LPARAM) As Long
Dim dwProcessId As DWord
    GetWindowThreadProcessId(hWnd,VarPtr(dwProcessId))
    If dwProcessId=lParam And IsWindowVisible(hWnd)<>0 Then ShowWindow(hWnd,SW_RESTORE)
    EnumWindowsProc=1
End Function
そしてお目当てのウィンドウが見つかったらShowWindow関数を呼び出して
アクティブにしています。

この場合、ウィンドウを所有しているスレッドが属するプロセスIDが等しいかどうかで判断しているようですね。
よって、hira様のプログラムは非常に安全性が高い方法だと言えます。

ウィンドウ名やウィンドウクラスで調べる方法もありますが、
その場合、同じ名前のウィンドウが存在すると致命的なバグが発生します。
konisi さんが書きました:> そもそも私はミューテックスがどのようなものか知らずにレス返してましたが、BackSearchABで調べたらGetLastError関数で確実に183(ERROR_ALREADY_EXISTS)が返るようにするものかなと解釈したところです。間違ってたら指摘してやってください。
イメージとしてはそれで合っています。


~余談~

私の場合、共有メモリで実現します。

最初に起動したアプリケーションでは共有メモリを新規作成し、
共有メモリに自分のウィンドウハンドルを書き込んでおきます。

次に実行されるプログラムでは共有メモリの新規作成に失敗するので、
そのタイミングで共有メモリからウィンドウハンドルを取り出して、
アクティブ化した後、自分自身を終了させるという方法です。
konisi
記事: 893
登録日時: 2005年7月25日(月) 13:27
お住まい: 埼玉県東松山市
連絡する:

ありがとうございました。

#12 投稿記事 by konisi »

なんとなく分かっていました。皆さん、ありがとうございました。
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。

に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
返信する